swift-code-reviewer-skill 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +44 -162
  2. package/README.md +91 -21
  3. package/SKILL.md +107 -725
  4. package/bin/install.js +87 -22
  5. package/package.json +16 -2
  6. package/references/companion-skills.md +70 -0
  7. package/skills/README.md +43 -0
  8. package/skills/swift-concurrency/NOTICE.md +18 -0
  9. package/skills/swift-concurrency/SKILL.md +235 -0
  10. package/skills/swift-concurrency/references/actors.md +640 -0
  11. package/skills/swift-concurrency/references/async-await-basics.md +249 -0
  12. package/skills/swift-concurrency/references/async-sequences.md +635 -0
  13. package/skills/swift-concurrency/references/core-data.md +533 -0
  14. package/skills/swift-concurrency/references/glossary.md +96 -0
  15. package/skills/swift-concurrency/references/linting.md +38 -0
  16. package/skills/swift-concurrency/references/memory-management.md +542 -0
  17. package/skills/swift-concurrency/references/migration.md +721 -0
  18. package/skills/swift-concurrency/references/performance.md +574 -0
  19. package/skills/swift-concurrency/references/sendable.md +578 -0
  20. package/skills/swift-concurrency/references/tasks.md +604 -0
  21. package/skills/swift-concurrency/references/testing.md +565 -0
  22. package/skills/swift-concurrency/references/threading.md +452 -0
  23. package/skills/swift-expert/NOTICE.md +18 -0
  24. package/skills/swift-expert/SKILL.md +226 -0
  25. package/skills/swift-expert/references/async-concurrency.md +363 -0
  26. package/skills/swift-expert/references/memory-performance.md +380 -0
  27. package/skills/swift-expert/references/protocol-oriented.md +357 -0
  28. package/skills/swift-expert/references/swiftui-patterns.md +294 -0
  29. package/skills/swift-expert/references/testing-patterns.md +402 -0
  30. package/skills/swift-testing/NOTICE.md +18 -0
  31. package/skills/swift-testing/SKILL.md +295 -0
  32. package/skills/swift-testing/references/async-testing.md +245 -0
  33. package/skills/swift-testing/references/dump-snapshot-testing.md +265 -0
  34. package/skills/swift-testing/references/fixtures.md +193 -0
  35. package/skills/swift-testing/references/integration-testing.md +189 -0
  36. package/skills/swift-testing/references/migration-xctest.md +301 -0
  37. package/skills/swift-testing/references/parameterized-tests.md +171 -0
  38. package/skills/swift-testing/references/snapshot-testing.md +201 -0
  39. package/skills/swift-testing/references/test-doubles.md +243 -0
  40. package/skills/swift-testing/references/test-organization.md +231 -0
  41. package/skills/swiftui-expert-skill/NOTICE.md +18 -0
  42. package/skills/swiftui-expert-skill/SKILL.md +281 -0
  43. package/skills/swiftui-expert-skill/references/accessibility-patterns.md +151 -0
  44. package/skills/swiftui-expert-skill/references/animation-advanced.md +403 -0
  45. package/skills/swiftui-expert-skill/references/animation-basics.md +284 -0
  46. package/skills/swiftui-expert-skill/references/animation-transitions.md +326 -0
  47. package/skills/swiftui-expert-skill/references/charts-accessibility.md +135 -0
  48. package/skills/swiftui-expert-skill/references/charts.md +602 -0
  49. package/skills/swiftui-expert-skill/references/image-optimization.md +203 -0
  50. package/skills/swiftui-expert-skill/references/latest-apis.md +464 -0
  51. package/skills/swiftui-expert-skill/references/layout-best-practices.md +266 -0
  52. package/skills/swiftui-expert-skill/references/liquid-glass.md +414 -0
  53. package/skills/swiftui-expert-skill/references/list-patterns.md +394 -0
  54. package/skills/swiftui-expert-skill/references/macos-scenes.md +318 -0
  55. package/skills/swiftui-expert-skill/references/macos-views.md +357 -0
  56. package/skills/swiftui-expert-skill/references/macos-window-styling.md +303 -0
  57. package/skills/swiftui-expert-skill/references/performance-patterns.md +403 -0
  58. package/skills/swiftui-expert-skill/references/scroll-patterns.md +293 -0
  59. package/skills/swiftui-expert-skill/references/sheet-navigation-patterns.md +363 -0
  60. package/skills/swiftui-expert-skill/references/state-management.md +417 -0
  61. package/skills/swiftui-expert-skill/references/view-structure.md +389 -0
  62. package/skills/swiftui-ui-patterns/NOTICE.md +18 -0
  63. package/skills/swiftui-ui-patterns/SKILL.md +95 -0
  64. package/skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
  65. package/skills/swiftui-ui-patterns/references/async-state.md +96 -0
  66. package/skills/swiftui-ui-patterns/references/components-index.md +50 -0
  67. package/skills/swiftui-ui-patterns/references/controls.md +57 -0
  68. package/skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
  69. package/skills/swiftui-ui-patterns/references/focus.md +90 -0
  70. package/skills/swiftui-ui-patterns/references/form.md +97 -0
  71. package/skills/swiftui-ui-patterns/references/grids.md +71 -0
  72. package/skills/swiftui-ui-patterns/references/haptics.md +71 -0
  73. package/skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  74. package/skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  75. package/skills/swiftui-ui-patterns/references/list.md +86 -0
  76. package/skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  77. package/skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
  78. package/skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  79. package/skills/swiftui-ui-patterns/references/media.md +73 -0
  80. package/skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
  81. package/skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
  82. package/skills/swiftui-ui-patterns/references/overlay.md +45 -0
  83. package/skills/swiftui-ui-patterns/references/performance.md +62 -0
  84. package/skills/swiftui-ui-patterns/references/previews.md +48 -0
  85. package/skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  86. package/skills/swiftui-ui-patterns/references/scrollview.md +87 -0
  87. package/skills/swiftui-ui-patterns/references/searchable.md +71 -0
  88. package/skills/swiftui-ui-patterns/references/sheets.md +155 -0
  89. package/skills/swiftui-ui-patterns/references/split-views.md +72 -0
  90. package/skills/swiftui-ui-patterns/references/tabview.md +114 -0
  91. package/skills/swiftui-ui-patterns/references/theming.md +71 -0
  92. package/skills/swiftui-ui-patterns/references/title-menus.md +93 -0
  93. package/skills/swiftui-ui-patterns/references/top-bar.md +49 -0
  94. package/templates/agents/swift-code-reviewer.md +78 -0
  95. package/templates/commands/review.md +56 -0
@@ -0,0 +1,389 @@
1
+ # SwiftUI View Structure Reference
2
+
3
+ ## Table of Contents
4
+
5
+ - [View Structure Principles](#view-structure-principles)
6
+ - [Prefer Modifiers Over Conditional Views](#prefer-modifiers-over-conditional-views)
7
+ - [Extract Subviews, Not Computed Properties](#extract-subviews-not-computed-properties)
8
+ - [When @ViewBuilder Functions Are Acceptable](#when-viewbuilder-functions-are-acceptable)
9
+ - [When to Extract Subviews](#when-to-extract-subviews)
10
+ - [Container View Pattern](#container-view-pattern)
11
+ - [ZStack vs overlay/background](#zstack-vs-overlaybackground)
12
+ - [Compositing Group Before Clipping](#compositing-group-before-clipping)
13
+ - [Reusable Styling with ViewModifier](#reusable-styling-with-viewmodifier)
14
+ - [Skeleton Loading with Redacted Views](#skeleton-loading-with-redacted-views)
15
+ - [UIViewRepresentable Essentials](#uiviewrepresentable-essentials)
16
+ - [Summary Checklist](#summary-checklist)
17
+
18
+ ## View Structure Principles
19
+
20
+ SwiftUI's diffing algorithm compares view hierarchies to determine what needs updating. Proper view composition directly impacts performance.
21
+
22
+ ## Prefer Modifiers Over Conditional Views
23
+
24
+ **Prefer "no-effect" modifiers over conditionally including views.** When you introduce a branch, consider whether you're representing multiple views or two states of the same view.
25
+
26
+ ### Use Opacity Instead of Conditional Inclusion
27
+
28
+ ```swift
29
+ // Good - same view, different states
30
+ SomeView()
31
+ .opacity(isVisible ? 1 : 0)
32
+
33
+ // Avoid - creates/destroys view identity
34
+ if isVisible {
35
+ SomeView()
36
+ }
37
+ ```
38
+
39
+ **Why**: Conditional view inclusion can cause loss of state, poor animation performance, and breaks view identity. Using modifiers maintains view identity across state changes.
40
+
41
+ ### When Conditionals Are Appropriate
42
+
43
+ Use conditionals when you truly have **different views**, not different states:
44
+
45
+ ```swift
46
+ // Correct - fundamentally different views
47
+ if isLoggedIn {
48
+ DashboardView()
49
+ } else {
50
+ LoginView()
51
+ }
52
+
53
+ // Correct - optional content
54
+ if let user {
55
+ UserProfileView(user: user)
56
+ }
57
+ ```
58
+
59
+ ### Conditional View Modifier Extensions Break Identity
60
+
61
+ A common pattern is an `if`-based `View` extension for conditional modifiers. This changes the view's return type between branches, which destroys view identity and breaks animations:
62
+
63
+ ```swift
64
+ // Problematic -- different return types per branch
65
+ extension View {
66
+ @ViewBuilder func `if`<T: View>(_ condition: Bool, transform: (Self) -> T) -> some View {
67
+ if condition {
68
+ transform(self) // Returns T
69
+ } else {
70
+ self // Returns Self
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ Prefer applying the modifier directly with a ternary or always-present modifier:
77
+
78
+ ```swift
79
+ // Good -- same view identity maintained
80
+ Text("Hello")
81
+ .opacity(isHighlighted ? 1 : 0.5)
82
+
83
+ // Good -- modifier always present, value changes
84
+ Text("Hello")
85
+ .foregroundStyle(isError ? .red : .primary)
86
+ ```
87
+
88
+ ## Extract Subviews, Not Computed Properties
89
+
90
+ ### The Problem with @ViewBuilder Functions
91
+
92
+ When you use `@ViewBuilder` functions or computed properties for complex views, the entire function re-executes on every parent state change:
93
+
94
+ ```swift
95
+ // BAD - re-executes complexSection() on every tap
96
+ struct ParentView: View {
97
+ @State private var count = 0
98
+
99
+ var body: some View {
100
+ VStack {
101
+ Button("Tap: \(count)") { count += 1 }
102
+ complexSection() // Re-executes every tap!
103
+ }
104
+ }
105
+
106
+ @ViewBuilder
107
+ func complexSection() -> some View {
108
+ // Complex views that re-execute unnecessarily
109
+ ForEach(0..<100) { i in
110
+ HStack {
111
+ Image(systemName: "star")
112
+ Text("Item \(i)")
113
+ Spacer()
114
+ Text("Detail")
115
+ }
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### The Solution: Separate Structs
122
+
123
+ Extract to separate `struct` views. SwiftUI can skip their `body` when inputs don't change:
124
+
125
+ ```swift
126
+ // GOOD - ComplexSection body SKIPPED when its inputs don't change
127
+ struct ParentView: View {
128
+ @State private var count = 0
129
+
130
+ var body: some View {
131
+ VStack {
132
+ Button("Tap: \(count)") { count += 1 }
133
+ ComplexSection() // Body skipped during re-evaluation
134
+ }
135
+ }
136
+ }
137
+
138
+ struct ComplexSection: View {
139
+ var body: some View {
140
+ ForEach(0..<100) { i in
141
+ HStack {
142
+ Image(systemName: "star")
143
+ Text("Item \(i)")
144
+ Spacer()
145
+ Text("Detail")
146
+ }
147
+ }
148
+ }
149
+ }
150
+ ```
151
+
152
+ ### Why This Works
153
+
154
+ 1. SwiftUI compares the `ComplexSection` struct (which has no properties)
155
+ 2. Since nothing changed, SwiftUI skips calling `ComplexSection.body`
156
+ 3. The complex view code never executes unnecessarily
157
+
158
+ ## When @ViewBuilder Functions Are Acceptable
159
+
160
+ Use for small, simple sections (a few views, no expensive computation) that don't affect performance. `@ViewBuilder` functions work particularly well for static content that doesn't depend on any `@State` or `@Binding`, since SwiftUI won't need to diff them independently. Extract to a separate `struct` when the section is complex, depends on state, or needs to be skipped during re-evaluation.
161
+
162
+ ## When to Extract Subviews
163
+
164
+ Extract complex views into separate subviews when:
165
+ - The view has multiple logical sections or responsibilities
166
+ - The view contains reusable components
167
+ - The view body becomes difficult to read or understand
168
+ - You need to isolate state changes for performance
169
+ - The view is becoming large (keep views small for better performance)
170
+
171
+ ## Container View Pattern
172
+
173
+ ### Avoid Closure-Based Content
174
+
175
+ Closures can't be compared, causing unnecessary re-renders:
176
+
177
+ ```swift
178
+ // BAD - closure prevents SwiftUI from skipping updates
179
+ struct MyContainer<Content: View>: View {
180
+ let content: () -> Content
181
+
182
+ var body: some View {
183
+ VStack {
184
+ Text("Header")
185
+ content() // Always called, can't compare closures
186
+ }
187
+ }
188
+ }
189
+
190
+ // Usage forces re-render on every parent update
191
+ MyContainer {
192
+ ExpensiveView()
193
+ }
194
+ ```
195
+
196
+ ### Use @ViewBuilder Property Instead
197
+
198
+ ```swift
199
+ // GOOD - view can be compared
200
+ struct MyContainer<Content: View>: View {
201
+ @ViewBuilder let content: Content
202
+
203
+ var body: some View {
204
+ VStack {
205
+ Text("Header")
206
+ content // SwiftUI can compare and skip if unchanged
207
+ }
208
+ }
209
+ }
210
+
211
+ // Usage - SwiftUI can diff ExpensiveView
212
+ MyContainer {
213
+ ExpensiveView()
214
+ }
215
+ ```
216
+
217
+ ## ZStack vs overlay/background
218
+
219
+ Use `ZStack` to **compose multiple peer views** that should be layered together and jointly define layout.
220
+
221
+ Prefer `overlay` / `background` when you’re **decorating a primary view**.
222
+ Not primarily because they don’t affect layout size, but because they **express intent and improve readability**: the view being modified remains the clear layout anchor.
223
+
224
+ A key difference is **size proposal behavior**:
225
+ - In `overlay` / `background`, the child view implicitly adopts the size proposed to the parent when it doesn’t define its own size, making decorative attachments feel natural and predictable.
226
+ - In `ZStack`, each child participates independently in layout, and no implicit size inheritance exists. This makes it better suited for peer composition, but less intuitive for simple decoration.
227
+
228
+ Use `ZStack` (or another container) when the “decoration” **must explicitly participate in layout sizing**—for example, when reserving space, extending tappable/visible bounds, or preventing overlap with neighboring views.
229
+
230
+ ### Examples
231
+
232
+ ```swift
233
+ // GOOD - decoration via overlay (layout anchored to button)
234
+ Button("Continue") { }
235
+ .overlay(alignment: .trailing) {
236
+ Image(systemName: "lock.fill").padding(.trailing, 8)
237
+ }
238
+
239
+ // BAD - ZStack when overlay suffices (layout no longer anchored to button)
240
+ ZStack(alignment: .trailing) {
241
+ Button("Continue") { }
242
+ Image(systemName: "lock.fill").padding(.trailing, 8)
243
+ }
244
+
245
+ // GOOD - background shape takes parent size
246
+ HStack(spacing: 12) { Text("Inbox"); Text("Next") }
247
+ .background { Capsule().strokeBorder(.blue, lineWidth: 2) }
248
+ ```
249
+
250
+ ## Compositing Group Before Clipping
251
+
252
+ **Always add `.compositingGroup()` before `.clipShape()` when clipping layered views (`.overlay` or `.background`).** Without it, each layer is antialiased separately and then composited. Where antialiased edges overlap — typically at rounded corners — you get visible color fringes (semi-transparent pixels of different colors blending together).
253
+
254
+ ```swift
255
+ let shape = RoundedRectangle(cornerRadius: 16)
256
+
257
+ // BAD - each layer antialiased separately, producing color fringes at corners
258
+ Color.red
259
+ .overlay(.white, in: shape)
260
+ .clipShape(shape)
261
+ .frame(width: 200, height: 150)
262
+
263
+ // GOOD - layers composited first, antialiasing applied once during clipping
264
+ Color.red
265
+ .overlay(.white, in: .rect)
266
+ .compositingGroup()
267
+ .clipShape(shape)
268
+ .frame(width: 200, height: 150)
269
+ ```
270
+
271
+ `.compositingGroup()` forces all child layers to be rendered into a single offscreen buffer before the clip is applied. This means antialiasing only happens once — on the final composited result — eliminating the fringe artifacts.
272
+
273
+ ## Reusable Styling with ViewModifier
274
+
275
+ Extract repeated modifier combinations into a `ViewModifier` struct. Expose via a `View` extension for autocompletion:
276
+
277
+ ```swift
278
+ private struct CardStyle: ViewModifier {
279
+ func body(content: Content) -> some View {
280
+ content
281
+ .padding()
282
+ .background(Color(.secondarySystemBackground))
283
+ .clipShape(.rect(cornerRadius: 12))
284
+ }
285
+ }
286
+
287
+ extension View {
288
+ func cardStyle() -> some View {
289
+ modifier(CardStyle())
290
+ }
291
+ }
292
+ ```
293
+
294
+ ### Custom ButtonStyle
295
+
296
+ Use the `ButtonStyle` protocol for reusable button designs. Use `PrimitiveButtonStyle` only when you need custom interaction handling (e.g., simultaneous gestures):
297
+
298
+ ```swift
299
+ struct PrimaryButtonStyle: ButtonStyle {
300
+ func makeBody(configuration: Configuration) -> some View {
301
+ configuration.label
302
+ .bold()
303
+ .foregroundStyle(.white)
304
+ .padding(.horizontal, 16)
305
+ .padding(.vertical, 8)
306
+ .background(Color.accentColor)
307
+ .clipShape(Capsule())
308
+ .scaleEffect(configuration.isPressed ? 0.95 : 1)
309
+ .animation(.smooth, value: configuration.isPressed)
310
+ }
311
+ }
312
+ ```
313
+
314
+ ### Discoverability with Static Member Lookup
315
+
316
+ Make custom styles and modifiers discoverable via leading-dot syntax:
317
+
318
+ ```swift
319
+ extension ButtonStyle where Self == PrimaryButtonStyle {
320
+ static var primary: PrimaryButtonStyle { .init() }
321
+ }
322
+
323
+ // Usage: .buttonStyle(.primary)
324
+ ```
325
+
326
+ This pattern works for any SwiftUI style protocol (`ButtonStyle`, `ListStyle`, `ToggleStyle`, etc.).
327
+
328
+ ## Skeleton Loading with Redacted Views
329
+
330
+ Use `.redacted(reason: .placeholder)` to show skeleton views while data loads. Use `.unredacted()` to opt out specific views:
331
+
332
+ ```swift
333
+ VStack(alignment: .leading) {
334
+ Text(article?.title ?? String(repeating: "X", count: 20))
335
+ .font(.headline)
336
+ Text(article?.author ?? String(repeating: "X", count: 12))
337
+ .font(.subheadline)
338
+ Text("SwiftLee")
339
+ .font(.caption)
340
+ .unredacted()
341
+ }
342
+ .redacted(reason: article == nil ? .placeholder : [])
343
+ ```
344
+
345
+ Apply `.redacted` on a container to redact all children at once.
346
+
347
+ ## UIViewRepresentable Essentials
348
+
349
+ When bridging UIKit views into SwiftUI:
350
+
351
+ - `makeUIView(context:)` is called **once** to create the UIKit view
352
+ - `updateUIView(_:context:)` is called on **every SwiftUI redraw** to sync state
353
+ - The representable struct itself is **recreated on every redraw** -- avoid heavy work in its init
354
+ - Use a `Coordinator` for delegate callbacks and two-way communication
355
+
356
+ ```swift
357
+ struct MapView: UIViewRepresentable {
358
+ let coordinate: CLLocationCoordinate2D
359
+
360
+ func makeUIView(context: Context) -> MKMapView {
361
+ let map = MKMapView()
362
+ map.delegate = context.coordinator
363
+ return map
364
+ }
365
+
366
+ func updateUIView(_ map: MKMapView, context: Context) {
367
+ map.setCenter(coordinate, animated: true)
368
+ }
369
+
370
+ func makeCoordinator() -> Coordinator { Coordinator() }
371
+
372
+ class Coordinator: NSObject, MKMapViewDelegate { }
373
+ }
374
+ ```
375
+
376
+ ## Summary Checklist
377
+
378
+ - [ ] Prefer modifiers over conditional views for state changes
379
+ - [ ] Avoid `if`-based conditional modifier extensions (they break view identity)
380
+ - [ ] Complex views extracted to separate subviews
381
+ - [ ] Views kept small for better performance
382
+ - [ ] `@ViewBuilder` functions only for simple sections
383
+ - [ ] Container views use `@ViewBuilder let content: Content`
384
+ - [ ] Extract views when they have multiple responsibilities or become hard to read
385
+ - [ ] Reusable styling extracted into `ViewModifier` or `ButtonStyle`
386
+ - [ ] Custom styles exposed via static member lookup for discoverability
387
+ - [ ] Use `.redacted(reason: .placeholder)` for skeleton loading states
388
+ - [ ] `.compositingGroup()` before `.clipShape()` on layered views (overlay/background) to avoid antialiasing fringes
389
+ - [ ] UIViewRepresentable: heavy work in make/update, not in struct init
@@ -0,0 +1,18 @@
1
+ # swiftui-ui-patterns — Attribution Notice
2
+
3
+ Bundled into `swift-code-reviewer-skill` on 2026-04-21 from
4
+ `~/.maestro/skills/swiftui-ui-patterns`.
5
+
6
+ Primary author (best-effort attribution): **Thomas Ricouard ([@Dimillian](https://github.com/Dimillian))**
7
+ Also credited: [@AvdLee](https://github.com/AvdLee), [@bocato](https://github.com/bocato)
8
+
9
+ These three authors' public Swift/SwiftUI content — including IceCubesApp, SwiftLee,
10
+ and their various open-source contributions — informed the skills bundled here.
11
+
12
+ License: the upstream folder did not contain a LICENSE file at the time of vendoring.
13
+ Content is reproduced here in good faith for reference alongside this MIT-licensed
14
+ project. If you are an upstream author and want the attribution corrected, the license
15
+ clarified, or the content removed, please open an issue at:
16
+ https://github.com/Viniciuscarvalho/swift-code-reviewer-skill/issues
17
+
18
+ Changes from upstream: none (verbatim copy; `.DS_Store` files excluded).
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: swiftui-ui-patterns
3
+ description: Best practices and example-driven guidance for building SwiftUI views and components, including navigation hierarchies, custom view modifiers, and responsive layouts with stacks and grids. Use when creating or refactoring SwiftUI UI, designing tab architecture with TabView, composing screens with VStack/HStack, managing @State or @Binding, building declarative iOS interfaces, or needing component-specific patterns and examples.
4
+ ---
5
+
6
+ # SwiftUI UI Patterns
7
+
8
+ ## Quick start
9
+
10
+ Choose a track based on your goal:
11
+
12
+ ### Existing project
13
+
14
+ - Identify the feature or screen and the primary interaction model (list, detail, editor, settings, tabbed).
15
+ - Find a nearby example in the repo with `rg "TabView\("` or similar, then read the closest SwiftUI view.
16
+ - Apply local conventions: prefer SwiftUI-native state, keep state local when possible, and use environment injection for shared dependencies.
17
+ - Choose the relevant component reference from `references/components-index.md` and follow its guidance.
18
+ - If the interaction reveals secondary content by dragging or scrolling the primary content away, read `references/scroll-reveal.md` before implementing gestures manually.
19
+ - Build the view with small, focused subviews and SwiftUI-native data flow.
20
+
21
+ ### New project scaffolding
22
+
23
+ - Start with `references/app-wiring.md` to wire TabView + NavigationStack + sheets.
24
+ - Add a minimal `AppTab` and `RouterPath` based on the provided skeletons.
25
+ - Choose the next component reference based on the UI you need first (TabView, NavigationStack, Sheets).
26
+ - Expand the route and sheet enums as new screens are added.
27
+
28
+ ## General rules to follow
29
+
30
+ - Use modern SwiftUI state (`@State`, `@Binding`, `@Observable`, `@Environment`) and avoid unnecessary view models.
31
+ - If the deployment target includes iOS 16 or earlier and cannot use the Observation API introduced in iOS 17, fall back to `ObservableObject` with `@StateObject` for root ownership, `@ObservedObject` for injected observation, and `@EnvironmentObject` only for truly shared app-level state.
32
+ - Prefer composition; keep views small and focused.
33
+ - Use async/await with `.task` and explicit loading/error states. For restart, cancellation, and debouncing guidance, read `references/async-state.md`.
34
+ - Keep shared app services in `@Environment`, but prefer explicit initializer injection for feature-local dependencies and models. For root wiring patterns, read `references/app-wiring.md`.
35
+ - Prefer the newest SwiftUI API that fits the deployment target and call out the minimum OS whenever a pattern depends on it.
36
+ - Maintain existing legacy patterns only when editing legacy files.
37
+ - Follow the project's formatter and style guide.
38
+ - **Sheets**: Prefer `.sheet(item:)` over `.sheet(isPresented:)` when state represents a selected model. Avoid `if let` inside a sheet body. Sheets should own their actions and call `dismiss()` internally instead of forwarding `onCancel`/`onConfirm` closures.
39
+ - **Scroll-driven reveals**: Prefer deriving a normalized progress value from scroll offset and driving the visual state from that single source of truth. Avoid parallel gesture state machines unless scroll alone cannot express the interaction.
40
+
41
+ ## State ownership summary
42
+
43
+ Use the narrowest state tool that matches the ownership model:
44
+
45
+ | Scenario | Preferred pattern |
46
+ | --- | --- |
47
+ | Local UI state owned by one view | `@State` |
48
+ | Child mutates parent-owned value state | `@Binding` |
49
+ | Root-owned reference model on iOS 17+ | `@State` with an `@Observable` type |
50
+ | Child reads or mutates an injected `@Observable` model on iOS 17+ | Pass it explicitly as a stored property |
51
+ | Shared app service or configuration | `@Environment(Type.self)` |
52
+ | Legacy reference model on iOS 16 and earlier | `@StateObject` at the root, `@ObservedObject` when injected |
53
+
54
+ Choose the ownership location first, then pick the wrapper. Do not introduce a reference model when plain value state is enough.
55
+
56
+ ## Cross-cutting references
57
+
58
+ - `references/navigationstack.md`: navigation ownership, per-tab history, and enum routing.
59
+ - `references/sheets.md`: centralized modal presentation and enum-driven sheets.
60
+ - `references/deeplinks.md`: URL handling and routing external links into app destinations.
61
+ - `references/app-wiring.md`: root dependency graph, environment usage, and app shell wiring.
62
+ - `references/async-state.md`: `.task`, `.task(id:)`, cancellation, debouncing, and async UI state.
63
+ - `references/previews.md`: `#Preview`, fixtures, mock environments, and isolated preview setup.
64
+ - `references/performance.md`: stable identity, observation scope, lazy containers, and render-cost guardrails.
65
+
66
+ ## Anti-patterns
67
+
68
+ - Giant views that mix layout, business logic, networking, routing, and formatting in one file.
69
+ - Multiple boolean flags for mutually exclusive sheets, alerts, or navigation destinations.
70
+ - Live service calls directly inside `body`-driven code paths instead of view lifecycle hooks or injected models/services.
71
+ - Reaching for `AnyView` to work around type mismatches that should be solved with better composition.
72
+ - Defaulting every shared dependency to `@EnvironmentObject` or a global router without a clear ownership reason.
73
+
74
+ ## Workflow for a new SwiftUI view
75
+
76
+ 1. Define the view's state, ownership location, and minimum OS assumptions before writing UI code.
77
+ 2. Identify which dependencies belong in `@Environment` and which should stay as explicit initializer inputs.
78
+ 3. Sketch the view hierarchy, routing model, and presentation points; extract repeated parts into subviews. For complex navigation, read `references/navigationstack.md`, `references/sheets.md`, or `references/deeplinks.md`. **Build and verify no compiler errors before proceeding.**
79
+ 4. Implement async loading with `.task` or `.task(id:)`, plus explicit loading and error states when needed. Read `references/async-state.md` when the work depends on changing inputs or cancellation.
80
+ 5. Add previews for the primary and secondary states, then add accessibility labels or identifiers when the UI is interactive. Read `references/previews.md` when the view needs fixtures or injected mock dependencies.
81
+ 6. Validate with a build: confirm no compiler errors, check that previews render without crashing, ensure state changes propagate correctly, and sanity-check that list identity and observation scope will not cause avoidable re-renders. Read `references/performance.md` if the screen is large, scroll-heavy, or frequently updated. For common SwiftUI compilation errors — missing `@State` annotations, ambiguous `ViewBuilder` closures, or mismatched generic types — resolve them before updating callsites. **If the build fails:** read the error message carefully, fix the identified issue, then rebuild before proceeding to the next step. If a preview crashes, isolate the offending subview, confirm its state initialisation is valid, and re-run the preview before continuing.
82
+
83
+ ## Component references
84
+
85
+ Use `references/components-index.md` as the entry point. Each component reference should include:
86
+ - Intent and best-fit scenarios.
87
+ - Minimal usage pattern with local conventions.
88
+ - Pitfalls and performance notes.
89
+ - Paths to existing examples in the current repo.
90
+
91
+ ## Adding a new component reference
92
+
93
+ - Create `references/<component>.md`.
94
+ - Keep it short and actionable; link to concrete files in the current repo.
95
+ - Update `references/components-index.md` with the new entry.