@kata-sh/cli 0.1.0 → 0.1.2

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 (199) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/app-paths.d.ts +4 -0
  4. package/dist/app-paths.js +6 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +56 -0
  7. package/dist/loader.d.ts +2 -0
  8. package/dist/loader.js +95 -0
  9. package/dist/resource-loader.d.ts +18 -0
  10. package/dist/resource-loader.js +50 -0
  11. package/dist/wizard.d.ts +15 -0
  12. package/dist/wizard.js +159 -0
  13. package/package.json +50 -21
  14. package/pkg/dist/modes/interactive/theme/dark.json +85 -0
  15. package/pkg/dist/modes/interactive/theme/light.json +84 -0
  16. package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
  17. package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
  18. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  19. package/pkg/dist/modes/interactive/theme/theme.js +949 -0
  20. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
  21. package/pkg/package.json +8 -0
  22. package/scripts/postinstall.js +45 -0
  23. package/src/resources/AGENTS.md +108 -0
  24. package/src/resources/KATA-WORKFLOW.md +661 -0
  25. package/src/resources/agents/researcher.md +29 -0
  26. package/src/resources/agents/scout.md +56 -0
  27. package/src/resources/agents/worker.md +31 -0
  28. package/src/resources/extensions/ask-user-questions.ts +200 -0
  29. package/src/resources/extensions/bg-shell/index.ts +2758 -0
  30. package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  31. package/src/resources/extensions/browser-tools/core.js +1057 -0
  32. package/src/resources/extensions/browser-tools/index.ts +4916 -0
  33. package/src/resources/extensions/browser-tools/package.json +20 -0
  34. package/src/resources/extensions/context7/index.ts +428 -0
  35. package/src/resources/extensions/context7/package.json +11 -0
  36. package/src/resources/extensions/get-secrets-from-user.ts +352 -0
  37. package/src/resources/extensions/github/formatters.ts +207 -0
  38. package/src/resources/extensions/github/gh-api.ts +537 -0
  39. package/src/resources/extensions/github/index.ts +778 -0
  40. package/src/resources/extensions/kata/activity-log.ts +88 -0
  41. package/src/resources/extensions/kata/auto.ts +2786 -0
  42. package/src/resources/extensions/kata/commands.ts +355 -0
  43. package/src/resources/extensions/kata/crash-recovery.ts +85 -0
  44. package/src/resources/extensions/kata/dashboard-overlay.ts +516 -0
  45. package/src/resources/extensions/kata/docs/preferences-reference.md +103 -0
  46. package/src/resources/extensions/kata/doctor.ts +683 -0
  47. package/src/resources/extensions/kata/files.ts +730 -0
  48. package/src/resources/extensions/kata/gitignore.ts +165 -0
  49. package/src/resources/extensions/kata/guided-flow.ts +976 -0
  50. package/src/resources/extensions/kata/index.ts +556 -0
  51. package/src/resources/extensions/kata/metrics.ts +397 -0
  52. package/src/resources/extensions/kata/observability-validator.ts +408 -0
  53. package/src/resources/extensions/kata/package.json +11 -0
  54. package/src/resources/extensions/kata/paths.ts +346 -0
  55. package/src/resources/extensions/kata/preferences.ts +695 -0
  56. package/src/resources/extensions/kata/prompt-loader.ts +50 -0
  57. package/src/resources/extensions/kata/prompts/complete-milestone.md +25 -0
  58. package/src/resources/extensions/kata/prompts/complete-slice.md +27 -0
  59. package/src/resources/extensions/kata/prompts/discuss.md +151 -0
  60. package/src/resources/extensions/kata/prompts/doctor-heal.md +29 -0
  61. package/src/resources/extensions/kata/prompts/execute-task.md +64 -0
  62. package/src/resources/extensions/kata/prompts/guided-complete-slice.md +1 -0
  63. package/src/resources/extensions/kata/prompts/guided-discuss-milestone.md +3 -0
  64. package/src/resources/extensions/kata/prompts/guided-discuss-slice.md +59 -0
  65. package/src/resources/extensions/kata/prompts/guided-execute-task.md +1 -0
  66. package/src/resources/extensions/kata/prompts/guided-plan-milestone.md +23 -0
  67. package/src/resources/extensions/kata/prompts/guided-plan-slice.md +1 -0
  68. package/src/resources/extensions/kata/prompts/guided-research-slice.md +11 -0
  69. package/src/resources/extensions/kata/prompts/guided-resume-task.md +1 -0
  70. package/src/resources/extensions/kata/prompts/plan-milestone.md +47 -0
  71. package/src/resources/extensions/kata/prompts/plan-slice.md +63 -0
  72. package/src/resources/extensions/kata/prompts/queue.md +85 -0
  73. package/src/resources/extensions/kata/prompts/reassess-roadmap.md +48 -0
  74. package/src/resources/extensions/kata/prompts/replan-slice.md +39 -0
  75. package/src/resources/extensions/kata/prompts/research-milestone.md +37 -0
  76. package/src/resources/extensions/kata/prompts/research-slice.md +28 -0
  77. package/src/resources/extensions/kata/prompts/run-uat.md +109 -0
  78. package/src/resources/extensions/kata/prompts/system.md +341 -0
  79. package/src/resources/extensions/kata/session-forensics.ts +550 -0
  80. package/src/resources/extensions/kata/skill-discovery.ts +137 -0
  81. package/src/resources/extensions/kata/state.ts +509 -0
  82. package/src/resources/extensions/kata/templates/context.md +76 -0
  83. package/src/resources/extensions/kata/templates/decisions.md +8 -0
  84. package/src/resources/extensions/kata/templates/milestone-summary.md +73 -0
  85. package/src/resources/extensions/kata/templates/plan.md +133 -0
  86. package/src/resources/extensions/kata/templates/preferences.md +15 -0
  87. package/src/resources/extensions/kata/templates/project.md +31 -0
  88. package/src/resources/extensions/kata/templates/reassessment.md +28 -0
  89. package/src/resources/extensions/kata/templates/requirements.md +81 -0
  90. package/src/resources/extensions/kata/templates/research.md +46 -0
  91. package/src/resources/extensions/kata/templates/roadmap.md +118 -0
  92. package/src/resources/extensions/kata/templates/slice-context.md +58 -0
  93. package/src/resources/extensions/kata/templates/slice-summary.md +99 -0
  94. package/src/resources/extensions/kata/templates/state.md +19 -0
  95. package/src/resources/extensions/kata/templates/task-plan.md +52 -0
  96. package/src/resources/extensions/kata/templates/task-summary.md +57 -0
  97. package/src/resources/extensions/kata/templates/uat.md +54 -0
  98. package/src/resources/extensions/kata/tests/activity-log-prune.test.ts +327 -0
  99. package/src/resources/extensions/kata/tests/auto-preflight.test.ts +97 -0
  100. package/src/resources/extensions/kata/tests/auto-supervisor.test.mjs +53 -0
  101. package/src/resources/extensions/kata/tests/complete-milestone.test.ts +317 -0
  102. package/src/resources/extensions/kata/tests/cost-projection.test.ts +160 -0
  103. package/src/resources/extensions/kata/tests/derive-state-deps.test.ts +477 -0
  104. package/src/resources/extensions/kata/tests/derive-state.test.ts +1013 -0
  105. package/src/resources/extensions/kata/tests/doctor.test.ts +718 -0
  106. package/src/resources/extensions/kata/tests/idle-recovery.test.ts +490 -0
  107. package/src/resources/extensions/kata/tests/metrics-io.test.ts +254 -0
  108. package/src/resources/extensions/kata/tests/metrics.test.ts +217 -0
  109. package/src/resources/extensions/kata/tests/must-have-parser.test.ts +309 -0
  110. package/src/resources/extensions/kata/tests/parsers.test.ts +1257 -0
  111. package/src/resources/extensions/kata/tests/plan-milestone.test.ts +185 -0
  112. package/src/resources/extensions/kata/tests/plan-quality-validator.test.ts +386 -0
  113. package/src/resources/extensions/kata/tests/reassess-prompt.test.ts +208 -0
  114. package/src/resources/extensions/kata/tests/replan-slice.test.ts +686 -0
  115. package/src/resources/extensions/kata/tests/requirements.test.ts +151 -0
  116. package/src/resources/extensions/kata/tests/resolve-ts-hooks.mjs +17 -0
  117. package/src/resources/extensions/kata/tests/resolve-ts.mjs +11 -0
  118. package/src/resources/extensions/kata/tests/run-uat.test.ts +383 -0
  119. package/src/resources/extensions/kata/tests/unit-runtime.test.ts +388 -0
  120. package/src/resources/extensions/kata/tests/workspace-index.test.ts +118 -0
  121. package/src/resources/extensions/kata/tests/worktree.test.ts +222 -0
  122. package/src/resources/extensions/kata/types.ts +159 -0
  123. package/src/resources/extensions/kata/unit-runtime.ts +163 -0
  124. package/src/resources/extensions/kata/workspace-index.ts +203 -0
  125. package/src/resources/extensions/kata/worktree.ts +182 -0
  126. package/src/resources/extensions/mac-tools/index.ts +852 -0
  127. package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  128. package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  129. package/src/resources/extensions/search-the-web/cache.ts +78 -0
  130. package/src/resources/extensions/search-the-web/format.ts +258 -0
  131. package/src/resources/extensions/search-the-web/http.ts +238 -0
  132. package/src/resources/extensions/search-the-web/index.ts +68 -0
  133. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
  134. package/src/resources/extensions/search-the-web/tool-llm-context.ts +404 -0
  135. package/src/resources/extensions/search-the-web/tool-search.ts +503 -0
  136. package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
  137. package/src/resources/extensions/shared/confirm-ui.ts +126 -0
  138. package/src/resources/extensions/shared/interview-ui.ts +822 -0
  139. package/src/resources/extensions/shared/next-action-ui.ts +235 -0
  140. package/src/resources/extensions/shared/progress-widget.ts +282 -0
  141. package/src/resources/extensions/shared/thinking-widget.ts +107 -0
  142. package/src/resources/extensions/shared/ui.ts +400 -0
  143. package/src/resources/extensions/shared/wizard-ui.ts +551 -0
  144. package/src/resources/extensions/slash-commands/audit.ts +92 -0
  145. package/src/resources/extensions/slash-commands/create-extension.ts +375 -0
  146. package/src/resources/extensions/slash-commands/create-slash-command.ts +280 -0
  147. package/src/resources/extensions/slash-commands/index.ts +12 -0
  148. package/src/resources/extensions/slash-commands/kata-run.ts +34 -0
  149. package/src/resources/extensions/subagent/agents.ts +126 -0
  150. package/src/resources/extensions/subagent/index.ts +1293 -0
  151. package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
  152. package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  153. package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  154. package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  155. package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  156. package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  157. package/src/resources/skills/frontend-design/SKILL.md +45 -0
  158. package/src/resources/skills/swiftui/SKILL.md +208 -0
  159. package/src/resources/skills/swiftui/references/animations.md +921 -0
  160. package/src/resources/skills/swiftui/references/architecture.md +1561 -0
  161. package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
  162. package/src/resources/skills/swiftui/references/navigation.md +1492 -0
  163. package/src/resources/skills/swiftui/references/networking-async.md +214 -0
  164. package/src/resources/skills/swiftui/references/performance.md +1706 -0
  165. package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
  166. package/src/resources/skills/swiftui/references/state-management.md +1443 -0
  167. package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
  168. package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
  169. package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  170. package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
  171. package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  172. package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  173. package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  174. package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
  175. package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
  176. package/dist/commands/task.d.ts +0 -9
  177. package/dist/commands/task.d.ts.map +0 -1
  178. package/dist/commands/task.js +0 -129
  179. package/dist/commands/task.js.map +0 -1
  180. package/dist/commands/task.test.d.ts +0 -2
  181. package/dist/commands/task.test.d.ts.map +0 -1
  182. package/dist/commands/task.test.js +0 -169
  183. package/dist/commands/task.test.js.map +0 -1
  184. package/dist/e2e/task-e2e.test.d.ts +0 -2
  185. package/dist/e2e/task-e2e.test.d.ts.map +0 -1
  186. package/dist/e2e/task-e2e.test.js +0 -173
  187. package/dist/e2e/task-e2e.test.js.map +0 -1
  188. package/dist/index.d.ts +0 -3
  189. package/dist/index.d.ts.map +0 -1
  190. package/dist/index.js +0 -93
  191. package/dist/index.js.map +0 -1
  192. package/dist/slug.d.ts +0 -2
  193. package/dist/slug.d.ts.map +0 -1
  194. package/dist/slug.js +0 -12
  195. package/dist/slug.js.map +0 -1
  196. package/dist/slug.test.d.ts +0 -2
  197. package/dist/slug.test.d.ts.map +0 -1
  198. package/dist/slug.test.js +0 -32
  199. package/dist/slug.test.js.map +0 -1
@@ -0,0 +1,297 @@
1
+ <overview>
2
+ SwiftData is Apple's modern persistence framework introduced at WWDC 2023, built on Core Data but with a Swift-native API. It provides declarative data modeling, automatic persistence, and seamless SwiftUI integration with minimal boilerplate.
3
+
4
+ **Key insight:** SwiftData eliminates the complexity of Core Data while maintaining its power. Where Core Data requires NSManagedObject subclasses, fetch request controllers, and entity descriptions, SwiftData uses Swift macros (@Model, @Query) and modern Swift features like #Predicate for compile-time validation.
5
+
6
+ **Minimum deployment:** iOS 17, macOS 14, watchOS 10, tvOS 17, visionOS 1.0
7
+
8
+ **When to read this file:**
9
+ - Persisting app data locally or syncing with iCloud
10
+ - Defining data models and relationships
11
+ - Querying and filtering stored data
12
+ - Migrating from Core Data to SwiftData
13
+ - Before reading: architecture.md (understand app structure), state-management.md (understand @Observable)
14
+ - Read alongside: platform-integration.md (for CloudKit integration details)
15
+ </overview>
16
+
17
+ <model_definition>
18
+ ## Defining Models
19
+
20
+ **@Model macro:**
21
+ ```swift
22
+ import SwiftData
23
+
24
+ @Model
25
+ class Item {
26
+ var name: String
27
+ var timestamp: Date
28
+ var isCompleted: Bool
29
+
30
+ init(name: String) {
31
+ self.name = name
32
+ self.timestamp = Date()
33
+ self.isCompleted = false
34
+ }
35
+ }
36
+ ```
37
+
38
+ The @Model macro transforms a Swift class into a SwiftData model. SwiftData automatically persists all stored properties.
39
+
40
+ **Supported property types:**
41
+ - Basic types: String, Int, Double, Bool, Date, UUID, URL, Data
42
+ - Codable types (stored as JSON)
43
+ - Collections: [String], [Int], etc.
44
+ - Relationships to other @Model types
45
+ - Optionals of any above type
46
+
47
+ **@Attribute options:**
48
+ ```swift
49
+ @Model
50
+ class User {
51
+ @Attribute(.unique) var id: UUID
52
+ @Attribute(.externalStorage) var profileImage: Data
53
+ @Attribute(.spotlight) var displayName: String
54
+ @Attribute(.allowsCloudEncryption) var sensitiveInfo: String
55
+
56
+ var email: String
57
+
58
+ init(id: UUID = UUID(), displayName: String, email: String) {
59
+ self.id = id
60
+ self.displayName = displayName
61
+ self.email = email
62
+ self.profileImage = Data()
63
+ self.sensitiveInfo = ""
64
+ }
65
+ }
66
+ ```
67
+
68
+ **@Transient for non-persisted properties:**
69
+ ```swift
70
+ @Model
71
+ class Task {
72
+ var title: String
73
+ var createdAt: Date
74
+
75
+ @Transient var isEditing: Bool = false
76
+
77
+ var ageInDays: Int {
78
+ Calendar.current.dateComponents([.day], from: createdAt, to: Date()).day ?? 0
79
+ }
80
+
81
+ init(title: String) {
82
+ self.title = title
83
+ self.createdAt = Date()
84
+ }
85
+ }
86
+ ```
87
+ </model_definition>
88
+
89
+ <relationships>
90
+ ## Relationships
91
+
92
+ **One-to-many:**
93
+ ```swift
94
+ @Model
95
+ class Folder {
96
+ var name: String
97
+ @Relationship(deleteRule: .cascade) var items: [Item] = []
98
+
99
+ init(name: String) {
100
+ self.name = name
101
+ }
102
+ }
103
+
104
+ @Model
105
+ class Item {
106
+ var name: String
107
+ var folder: Folder?
108
+
109
+ init(name: String, folder: Folder? = nil) {
110
+ self.name = name
111
+ self.folder = folder
112
+ }
113
+ }
114
+ ```
115
+
116
+ **Delete rules:**
117
+ - `.cascade` - deletes related objects
118
+ - `.nullify` - sets relationship to nil (default)
119
+ - `.deny` - prevents deletion if relationship exists
120
+ - `.noAction` - does nothing (use with caution)
121
+
122
+ **Inverse relationships:**
123
+ ```swift
124
+ @Model
125
+ class Author {
126
+ var name: String
127
+ @Relationship(inverse: \Book.author) var books: [Book] = []
128
+
129
+ init(name: String) {
130
+ self.name = name
131
+ }
132
+ }
133
+ ```
134
+ </relationships>
135
+
136
+ <model_container>
137
+ ## ModelContainer and ModelContext
138
+
139
+ **Setting up container in App:**
140
+ ```swift
141
+ import SwiftUI
142
+ import SwiftData
143
+
144
+ @main
145
+ struct MyApp: App {
146
+ var body: some Scene {
147
+ WindowGroup {
148
+ ContentView()
149
+ }
150
+ .modelContainer(for: [Item.self, Folder.self])
151
+ }
152
+ }
153
+ ```
154
+
155
+ **Custom configuration:**
156
+ ```swift
157
+ let config = ModelConfiguration(
158
+ schema: Schema([Item.self, Folder.self]),
159
+ url: URL.documentsDirectory.appending(path: "MyApp.store"),
160
+ cloudKitDatabase: .automatic
161
+ )
162
+
163
+ let container = try ModelContainer(
164
+ for: Item.self,
165
+ configurations: config
166
+ )
167
+ ```
168
+
169
+ **Accessing context in views:**
170
+ ```swift
171
+ @Environment(\.modelContext) private var context
172
+ ```
173
+ </model_container>
174
+
175
+ <querying>
176
+ ## Querying Data
177
+
178
+ **@Query in views:**
179
+ ```swift
180
+ @Query var items: [Item]
181
+
182
+ // With sorting
183
+ @Query(sort: \Item.timestamp, order: .reverse) var items: [Item]
184
+
185
+ // With filtering
186
+ @Query(filter: #Predicate<Item> { $0.isCompleted == false }) var items: [Item]
187
+ ```
188
+
189
+ **Dynamic queries:**
190
+ ```swift
191
+ struct SearchableItemList: View {
192
+ @Query var items: [Item]
193
+
194
+ init(searchText: String) {
195
+ let predicate = #Predicate<Item> { item in
196
+ searchText.isEmpty || item.name.localizedStandardContains(searchText)
197
+ }
198
+ _items = Query(filter: predicate)
199
+ }
200
+ }
201
+ ```
202
+
203
+ **FetchDescriptor for context queries:**
204
+ ```swift
205
+ let descriptor = FetchDescriptor<Item>(
206
+ predicate: #Predicate { $0.isCompleted },
207
+ sortBy: [SortDescriptor(\.timestamp)]
208
+ )
209
+ let items = try context.fetch(descriptor)
210
+ ```
211
+ </querying>
212
+
213
+ <crud_operations>
214
+ ## CRUD Operations
215
+
216
+ **Create:**
217
+ ```swift
218
+ let item = Item(name: "New Item")
219
+ context.insert(item)
220
+ ```
221
+
222
+ **Update:**
223
+ ```swift
224
+ item.name = "Updated Name"
225
+ // Changes auto-save
226
+ ```
227
+
228
+ **Delete:**
229
+ ```swift
230
+ context.delete(item)
231
+ ```
232
+
233
+ **Manual save:**
234
+ ```swift
235
+ try context.save()
236
+ ```
237
+ </crud_operations>
238
+
239
+ <cloudkit_sync>
240
+ ## CloudKit Sync
241
+
242
+ **Enable in container:**
243
+ ```swift
244
+ let config = ModelConfiguration(cloudKitDatabase: .automatic)
245
+ ```
246
+
247
+ **CloudKit constraints:**
248
+ - Cannot use @Attribute(.unique) with CloudKit
249
+ - All properties need defaults or be optional
250
+ - Relationships must be optional
251
+ - Private database only
252
+ </cloudkit_sync>
253
+
254
+ <migration>
255
+ ## Schema Migration
256
+
257
+ **Lightweight migration (automatic):**
258
+ - Adding properties with defaults
259
+ - Removing properties
260
+ - Renaming with @Attribute(originalName:)
261
+
262
+ **Schema versioning:**
263
+ ```swift
264
+ enum SchemaV1: VersionedSchema {
265
+ static var versionIdentifier = Schema.Version(1, 0, 0)
266
+ static var models: [any PersistentModel.Type] { [Item.self] }
267
+ }
268
+ ```
269
+ </migration>
270
+
271
+ <decision_tree>
272
+ ## Choosing Your Approach
273
+
274
+ **New project, iOS 17+ only:** SwiftData
275
+ **Need iOS 16 support:** Core Data
276
+ **Existing Core Data project:** Keep Core Data unless full migration planned
277
+ **Need CloudKit:** SwiftData (simpler) or Core Data (more control)
278
+ </decision_tree>
279
+
280
+ <anti_patterns>
281
+ ## What NOT to Do
282
+
283
+ <anti_pattern name="Using @Query outside SwiftUI views">
284
+ **Problem:** @Query requires SwiftUI environment
285
+ **Instead:** Use FetchDescriptor with explicit context in view models
286
+ </anti_pattern>
287
+
288
+ <anti_pattern name="Using @Attribute(.unique) with CloudKit">
289
+ **Problem:** Silently breaks CloudKit sync
290
+ **Instead:** Handle uniqueness in app logic
291
+ </anti_pattern>
292
+
293
+ <anti_pattern name="Transient properties in predicates">
294
+ **Problem:** Compiles but crashes at runtime
295
+ **Instead:** Use persisted properties for filtering
296
+ </anti_pattern>
297
+ </anti_patterns>
@@ -0,0 +1,247 @@
1
+ <overview>
2
+ Testing and debugging SwiftUI apps requires a multi-layered approach combining previews, unit tests, UI tests, and debugging tools. SwiftUI's declarative nature makes traditional debugging challenging, but modern tools provide robust solutions.
3
+
4
+ **Key principles:**
5
+ - Use #Preview macros for rapid visual iteration
6
+ - Test business logic with @Observable view models (not views directly)
7
+ - Write focused UI tests using accessibility identifiers
8
+ - Profile with Instruments on real devices
9
+
10
+ SwiftUI views cannot be unit tested directly. Test view models and use UI automation tests for interaction testing.
11
+ </overview>
12
+
13
+ <previews>
14
+ ## Xcode Previews
15
+
16
+ **Basic #Preview:**
17
+ ```swift
18
+ #Preview {
19
+ ContentView()
20
+ }
21
+
22
+ #Preview("Dark Mode") {
23
+ ContentView()
24
+ .preferredColorScheme(.dark)
25
+ }
26
+ ```
27
+
28
+ **Multiple states:**
29
+ ```swift
30
+ #Preview("Empty") { TaskListView(tasks: []) }
31
+ #Preview("Loaded") { TaskListView(tasks: Task.sampleData) }
32
+ #Preview("Error") { TaskListView(tasks: [], error: "Network unavailable") }
33
+ ```
34
+
35
+ **With @Binding (Xcode 16+):**
36
+ ```swift
37
+ #Preview {
38
+ @Previewable @State var isOn = true
39
+ ToggleView(isOn: $isOn)
40
+ }
41
+ ```
42
+
43
+ **Mock data:**
44
+ ```swift
45
+ extension Task {
46
+ static let sampleData: [Task] = [
47
+ Task(title: "Review PR", isCompleted: false),
48
+ Task(title: "Write tests", isCompleted: true)
49
+ ]
50
+ }
51
+ ```
52
+ </previews>
53
+
54
+ <unit_testing>
55
+ ## Unit Testing View Models
56
+
57
+ **Testing @Observable with Swift Testing:**
58
+ ```swift
59
+ import Testing
60
+ @testable import MyApp
61
+
62
+ @Test("Login validation")
63
+ func loginValidation() {
64
+ let viewModel = LoginViewModel()
65
+ viewModel.email = ""
66
+ viewModel.password = "password123"
67
+ #expect(!viewModel.isValidInput)
68
+
69
+ viewModel.email = "user@example.com"
70
+ #expect(viewModel.isValidInput)
71
+ }
72
+
73
+ @Test("Async data loading")
74
+ func dataLoading() async {
75
+ let mockService = MockService()
76
+ let viewModel = TaskViewModel(service: mockService)
77
+
78
+ await viewModel.load()
79
+
80
+ #expect(!viewModel.tasks.isEmpty)
81
+ }
82
+ ```
83
+
84
+ **Dependency injection for testing:**
85
+ ```swift
86
+ @Observable
87
+ final class TaskViewModel {
88
+ private let service: TaskServiceProtocol
89
+
90
+ init(service: TaskServiceProtocol = TaskService()) {
91
+ self.service = service
92
+ }
93
+ }
94
+ ```
95
+ </unit_testing>
96
+
97
+ <ui_testing>
98
+ ## UI Testing
99
+
100
+ **Setting accessibility identifiers:**
101
+ ```swift
102
+ TextField("Email", text: $email)
103
+ .accessibilityIdentifier("emailField")
104
+
105
+ Button("Login") { }
106
+ .accessibilityIdentifier("loginButton")
107
+ ```
108
+
109
+ **Writing UI tests:**
110
+ ```swift
111
+ import XCTest
112
+
113
+ final class LoginUITests: XCTestCase {
114
+ var app: XCUIApplication!
115
+
116
+ override func setUp() {
117
+ continueAfterFailure = false
118
+ app = XCUIApplication()
119
+ app.launch()
120
+ }
121
+
122
+ func testLoginFlow() {
123
+ let emailField = app.textFields["emailField"]
124
+ let loginButton = app.buttons["loginButton"]
125
+
126
+ XCTAssertTrue(emailField.waitForExistence(timeout: 5))
127
+ emailField.tap()
128
+ emailField.typeText("user@example.com")
129
+
130
+ loginButton.tap()
131
+
132
+ let welcomeText = app.staticTexts["welcomeMessage"]
133
+ XCTAssertTrue(welcomeText.waitForExistence(timeout: 5))
134
+ }
135
+ }
136
+ ```
137
+ </ui_testing>
138
+
139
+ <debugging>
140
+ ## Debugging Techniques
141
+
142
+ **_printChanges():**
143
+ ```swift
144
+ var body: some View {
145
+ let _ = Self._printChanges()
146
+ VStack { /* content */ }
147
+ }
148
+ ```
149
+
150
+ **View hierarchy debugger:**
151
+ Debug menu → View Debugging → Capture View Hierarchy
152
+
153
+ **Lifecycle debugging:**
154
+ ```swift
155
+ .onAppear { print("View appeared") }
156
+ .onDisappear { print("View disappeared") }
157
+ .task { print("Task started") }
158
+ ```
159
+
160
+ **Visual debugging:**
161
+ ```swift
162
+ .border(.red)
163
+ .background(.yellow.opacity(0.3))
164
+ ```
165
+ </debugging>
166
+
167
+ <instruments>
168
+ ## Instruments Profiling
169
+
170
+ **SwiftUI template (Xcode 16+):**
171
+ - View Body: Track view creation count
172
+ - View Properties: Monitor property changes
173
+ - Core Animation Commits: Animation performance
174
+
175
+ **Time Profiler:**
176
+ 1. Product → Profile (Cmd+I)
177
+ 2. Select Time Profiler
178
+ 3. Record while using app
179
+ 4. Sort by "Self" time to find hotspots
180
+
181
+ **Allocations:**
182
+ - Track memory usage
183
+ - Filter by "Persistent" to find leaks
184
+
185
+ **Always profile on real devices, not simulators.**
186
+ </instruments>
187
+
188
+ <common_bugs>
189
+ ## Common SwiftUI Bugs
190
+
191
+ **View not updating:**
192
+ ```swift
193
+ // Problem: missing @State
194
+ var count = 0 // Won't trigger updates
195
+
196
+ // Fix: use @State
197
+ @State private var count = 0
198
+ ```
199
+
200
+ **ForEach crash on empty binding:**
201
+ ```swift
202
+ // Problem: binding crashes on empty
203
+ ForEach($items) { $item in }
204
+
205
+ // Fix: check for empty
206
+ if !items.isEmpty {
207
+ ForEach($items) { $item in }
208
+ }
209
+ ```
210
+
211
+ **Animation not working:**
212
+ ```swift
213
+ // Problem: no value parameter
214
+ .animation(.spring())
215
+
216
+ // Fix: specify value
217
+ .animation(.spring(), value: isExpanded)
218
+ ```
219
+ </common_bugs>
220
+
221
+ <decision_tree>
222
+ ## Testing Strategy
223
+
224
+ **Preview:** Visual iteration, different states
225
+ **Unit Test:** @Observable view models, business logic
226
+ **UI Test:** Critical user flows, login, checkout
227
+ **Manual Test:** Animations, accessibility, performance
228
+ </decision_tree>
229
+
230
+ <anti_patterns>
231
+ ## What NOT to Do
232
+
233
+ <anti_pattern name="Testing view bodies">
234
+ **Problem:** Trying to unit test SwiftUI views directly
235
+ **Instead:** Extract logic to view models, test those
236
+ </anti_pattern>
237
+
238
+ <anti_pattern name="Missing accessibility identifiers">
239
+ **Problem:** Using text to find elements in UI tests
240
+ **Instead:** Use .accessibilityIdentifier("stableId")
241
+ </anti_pattern>
242
+
243
+ <anti_pattern name="No dependency injection">
244
+ **Problem:** Hardcoded dependencies in view models
245
+ **Instead:** Use protocols, inject mocks in tests
246
+ </anti_pattern>
247
+ </anti_patterns>