@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0

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 (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,357 +1,493 @@
1
1
  ---
2
2
  name: swiftdata
3
- description: "Implement, review, or improve data persistence using SwiftData. Use when defining @Model classes with @Attribute, @Relationship, @Transient, @Unique, or @Index; when querying with @Query, #Predicate, FetchDescriptor, or SortDescriptor; when configuring ModelContainer and ModelContext for SwiftUI or background work with @ModelActor; when planning schema migrations with VersionedSchema and SchemaMigrationPlan; when setting up CloudKit sync with ModelConfiguration; or when coexisting with or migrating from Core Data."
3
+ description: "SwiftData persistence for Apple apps: @Model classes with @Attribute, @Relationship, @Transient, #Unique and #Index; @Query, #Predicate, FetchDescriptor and SortDescriptor; ModelContainer and ModelContext setup in SwiftUI; @ModelActor background work; VersionedSchema and SchemaMigrationPlan migrations; CloudKit sync through ModelConfiguration; Core Data coexistence or migration to SwiftData. Use when implementing, reviewing or improving SwiftData persistence. Not for a standalone Core Data stack tutorial."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
8
  # SwiftData
9
9
 
10
- Persist, query, and manage structured data in iOS 26+ apps using SwiftData
11
- with Swift 6.3.
10
+ Baseline: apps targeting iOS 26 and later, built with Swift 6.3. Features that
11
+ arrived earlier are marked with their first OS so an older deployment target
12
+ can still use this skill.
13
+
14
+ SwiftData stores Swift classes directly. You describe the schema with macros on
15
+ the class, a `ModelContainer` owns the store, a `ModelContext` tracks edits, and
16
+ SwiftUI reads through `@Query`. Most bugs come from three places: predicates the
17
+ store cannot translate, model objects crossing actors, and schema changes
18
+ shipped without a migration plan.
12
19
 
13
20
  ## Contents
14
21
 
15
- - [Model Definition](#model-definition)
16
- - [ModelContainer Setup](#modelcontainer-setup)
17
- - [CloudKit Sync](#cloudkit-sync)
18
- - [CRUD Operations](#crud-operations)
19
- - [`@Query in SwiftUI`](#query-in-swiftui)
20
- - [#Predicate](#predicate)
22
+ - [Defining models](#defining-models)
23
+ - [Container setup](#container-setup)
24
+ - [CloudKit sync](#cloudkit-sync)
25
+ - [Create, read, update, delete](#create-read-update-delete)
26
+ - [@Query in views](#query-in-views)
27
+ - [Writing predicates](#writing-predicates)
21
28
  - [FetchDescriptor](#fetchdescriptor)
22
- - [Schema Versioning and Migration](#schema-versioning-and-migration)
23
- - [Concurrency (`@ModelActor`)](#concurrency-modelactor)
24
- - [SwiftUI Integration](#swiftui-integration)
25
- - [Common Mistakes](#common-mistakes)
26
- - [Review Checklist](#review-checklist)
29
+ - [Schema versions and migration](#schema-versions-and-migration)
30
+ - [Background work with @ModelActor](#background-work-with-modelactor)
31
+ - [SwiftUI wiring and previews](#swiftui-wiring-and-previews)
32
+ - [Mistakes to catch](#mistakes-to-catch)
33
+ - [Review checklist](#review-checklist)
27
34
  - [References](#references)
28
35
 
29
- ## Model Definition
36
+ ## Defining models
30
37
 
31
- Apply `@Model` to a **class** (not struct). Generates `PersistentModel`, `Observable`, `Sendable`.
38
+ `@Model` only applies to classes. It adds conformance to `PersistentModel`,
39
+ `Observable` and `Sendable`, so a model needs reference semantics and a
40
+ designated initializer.
32
41
 
33
42
  ```swift
43
+ import SwiftData
44
+ import Foundation
45
+
46
+ @Model
47
+ class Plant {
48
+ var commonName: String
49
+ var acquiredOn: Date
50
+ var needsRepotting: Bool = false
51
+ @Attribute(.externalStorage) var photo: Data?
52
+ @Relationship(deleteRule: .cascade, inverse: \CareEvent.plant)
53
+ var careEvents: [CareEvent] = []
54
+ var bed: GardenBed?
55
+ @Transient var isHighlighted: Bool = false
56
+
57
+ init(commonName: String, acquiredOn: Date = .now) {
58
+ self.commonName = commonName
59
+ self.acquiredOn = acquiredOn
60
+ }
61
+ }
62
+
63
+ @Model
64
+ final class GardenBed {
65
+ var label: String
66
+ @Relationship(deleteRule: .nullify, inverse: \Plant.bed)
67
+ var plants: [Plant] = []
68
+ init(label: String) { self.label = label }
69
+ }
70
+
34
71
  @Model
35
- class Trip {
36
- var name: String
37
- var destination: String
38
- var startDate: Date
39
- var endDate: Date
40
- var isFavorite: Bool = false
41
- @Attribute(.externalStorage) var imageData: Data?
42
- @Relationship(deleteRule: .cascade, inverse: \LivingAccommodation.trip)
43
- var accommodation: LivingAccommodation?
44
- @Transient var isSelected: Bool = false // Always provide default
45
-
46
- init(name: String, destination: String, startDate: Date, endDate: Date) {
47
- self.name = name; self.destination = destination
48
- self.startDate = startDate; self.endDate = endDate
72
+ final class CareEvent {
73
+ var kind: String
74
+ var loggedAt: Date
75
+ var plant: Plant?
76
+
77
+ init(kind: String, loggedAt: Date) {
78
+ self.kind = kind
79
+ self.loggedAt = loggedAt
49
80
  }
50
81
  }
51
82
  ```
52
83
 
53
- **`@Attribute` options**: `.externalStorage`, `.unique`, `.spotlight`, `.allowsCloudEncryption`, `.preserveValueOnDeletion` (iOS 18+), `.ephemeral`, `.transformable(by:)`. Rename: `@Attribute(originalName: "old_name")`.
84
+ Rules that follow from the macro:
85
+
86
+ - A `@Transient` property is never stored, so it needs a default value.
87
+ - `@Attribute` options: `.externalStorage` (large blobs beside the store),
88
+ `.unique`, `.spotlight`, `.allowsCloudEncryption`, `.ephemeral`,
89
+ `.transformable(by:)`, and `.preserveValueOnDeletion` (iOS 18+, keeps the
90
+ value in history tombstones).
91
+ - Renaming a stored property without losing data:
92
+ `@Attribute(originalName: "old_name") var newName: String`.
93
+ - `@Relationship(deleteRule:)` accepts `.cascade`, `.nullify` (the default),
94
+ `.deny` and `.noAction`. Give `inverse:` explicitly; inferred inverses are
95
+ where cascade and nullify stop behaving predictably.
96
+ - A one-way relationship (iOS 18+) is declared with `inverse: nil`.
97
+ - Compound uniqueness (iOS 18+) lives in the class body:
98
+ `#Unique<Plant>([\.commonName, \.acquiredOn])`.
99
+ - A `@Model` class can subclass another `@Model` class (iOS 26+):
54
100
 
55
- **`@Relationship`**: `deleteRule:` `.cascade`/`.nullify`(default)/`.deny`/`.noAction`. Specify `inverse:` for reliable behavior. Unidirectional (iOS 18+): `inverse: nil`.
101
+ ```swift
102
+ @available(iOS 26, *)
103
+ @Model
104
+ final class Succulent: Plant {
105
+ var waterEveryDays: Int = 14
56
106
 
57
- **#Unique (iOS 18+)**: `#Unique<Person>([\.firstName, \.lastName])` -- compound uniqueness.
107
+ init(commonName: String, waterEveryDays: Int) {
108
+ self.waterEveryDays = waterEveryDays
109
+ super.init(commonName: commonName)
110
+ }
111
+ }
112
+ ```
58
113
 
59
- **Inheritance (iOS 26+)**: `@Model class BusinessTrip: Trip { var company: String }`.
114
+ The base class is declared without `final` so it can be subclassed.
60
115
 
61
- Supported types: `Bool`, `Int`/`UInt` variants, `Float`, `Double`, `String`, `Date`, `Data`, `URL`, `UUID`, `Decimal`, `Array`, `Dictionary`, `Set`, `Codable` enums, `Codable` structs and other compatible `Codable` value types, and relationships to `@Model` classes.
116
+ Storable property types: `Bool`, every `Int` and `UInt` width, `Float`,
117
+ `Double`, `String`, `Date`, `Data`, `URL`, `UUID`, `Decimal`, arrays,
118
+ dictionaries and sets of storable values, `Codable` enums, `Codable` structs and
119
+ other compatible `Codable` value types, plus relationships to other `@Model`
120
+ classes. Codable value storage is not an iOS 18 feature; it has worked since
121
+ SwiftData first shipped. See
122
+ [references/swiftdata-advanced.md](references/swiftdata-advanced.md#codable-structs-as-composite-attributes)
123
+ for how structs are laid out.
62
124
 
63
- ## ModelContainer Setup
125
+ Indexes (`#Index`, iOS 18+) are covered in
126
+ [references/indexing.md](references/indexing.md).
127
+
128
+ ## Container setup
64
129
 
65
130
  ```swift
66
- // Basic
67
- let container = try ModelContainer(for: Trip.self, LivingAccommodation.self)
68
-
69
- // Configured
70
- let config = ModelConfiguration("Store", isStoredInMemoryOnly: false,
71
- groupContainer: .identifier("group.com.example.app"),
72
- cloudKitDatabase: .private("iCloud.com.example.app"))
73
- let container = try ModelContainer(for: Trip.self, configurations: config)
74
-
75
- // With migration plan
76
- let container = try ModelContainer(for: SchemaV2.Trip.self,
77
- migrationPlan: TripMigrationPlan.self)
78
-
79
- // In-memory (previews/tests)
80
- let container = try ModelContainer(for: Trip.self,
81
- configurations: ModelConfiguration(isStoredInMemoryOnly: true))
131
+ // Every model type the app stores, listed once.
132
+ let simple = try ModelContainer(for: Plant.self, CareEvent.self, GardenBed.self)
133
+
134
+ // Named store, shared with extensions, synced to a private CloudKit database.
135
+ let config = ModelConfiguration(
136
+ "Garden",
137
+ isStoredInMemoryOnly: false,
138
+ groupContainer: .identifier("group.example.garden"),
139
+ cloudKitDatabase: .private("iCloud.example.garden")
140
+ )
141
+ let configured = try ModelContainer(for: Plant.self, configurations: config)
142
+
143
+ // Versioned schema with a migration plan.
144
+ let migrating = try ModelContainer(
145
+ for: GardenSchemaV3.Plant.self,
146
+ migrationPlan: GardenMigrationPlan.self
147
+ )
148
+
149
+ // Tests and previews: nothing touches disk.
150
+ let scratch = try ModelContainer(
151
+ for: Plant.self,
152
+ configurations: ModelConfiguration(isStoredInMemoryOnly: true)
153
+ )
82
154
  ```
83
155
 
84
- ## CloudKit Sync
156
+ ## CloudKit sync
157
+
158
+ Setting `cloudKitDatabase:` on a configuration turns on automatic sync, but the
159
+ project entitlements decide whether sync actually runs. For any CloudKit setup
160
+ or schema review, give a Capabilities verdict first and the schema findings
161
+ after it.
162
+
163
+ Capabilities:
164
+
165
+ - The target has the iCloud capability with CloudKit checked and the intended
166
+ container selected.
167
+ - Background Modes has Remote notifications enabled.
168
+ - Without both, sync is not fully configured even though the code sets
169
+ `cloudKitDatabase`.
85
170
 
86
- `ModelConfiguration(..., cloudKitDatabase:)` opts a SwiftData store into
87
- automatic CloudKit sync, but app entitlements still gate sync.
171
+ Schema rules for a synced store:
88
172
 
89
- For any SwiftData CloudKit setup or schema-review task, include a separate
90
- **Capabilities** verdict before schema findings:
173
+ - No `@Attribute(.unique)` and no `#Unique`; CloudKit cannot enforce them.
174
+ - Relationships are optional, with explicit inverses where needed, and never use
175
+ `.deny`.
176
+ - Large `Data` uses `@Attribute(.externalStorage)`.
177
+ - Do not make every scalar optional for CloudKit's sake. A required scalar is
178
+ fine when its initializer, a default value or a migration always provides a
179
+ valid value.
91
180
 
92
- - **Capabilities**: Xcode target has the iCloud capability with CloudKit enabled
93
- and the intended container selected, plus Background Modes > Remote
94
- notifications. Without these entitlements, automatic sync is not fully
95
- configured even if `cloudKitDatabase` is set.
96
- - **Schema compatibility**: no `@Attribute(.unique)` or `#Unique`;
97
- relationships are optional, have explicit inverses where needed, and avoid
98
- `.deny`; large `Data` uses `@Attribute(.externalStorage)`.
99
- - **Scalar attributes**: do not make every scalar optional just for CloudKit.
100
- Keep required scalars nonoptional when initializers, defaults, or migrations
101
- provide valid values.
102
- - **Schema rollout**: initialize the development schema only in nonproduction
103
- builds, verify it in CloudKit Dashboard, promote before release, and treat
104
- production changes as additive only.
181
+ Rollout: initialize the development schema only from a non-production build,
182
+ inspect it in CloudKit Dashboard, deploy it to production before release, and
183
+ from then on make production schema changes additive only.
105
184
 
106
- ## CRUD Operations
185
+ ## Create, read, update, delete
107
186
 
108
187
  ```swift
109
- // CREATE
110
- let trip = Trip(name: "Summer", destination: "Paris", startDate: .now, endDate: .now + 86400*7)
111
- modelContext.insert(trip)
112
- try modelContext.save() // or rely on autosave
113
-
114
- // READ
115
- let trips = try modelContext.fetch(FetchDescriptor<Trip>(
116
- predicate: #Predicate { $0.destination == "Paris" },
117
- sortBy: [SortDescriptor(\.startDate)]))
118
-
119
- // UPDATE -- modify properties directly; autosave handles persistence
120
- trip.destination = "Rome"
121
-
122
- // DELETE
123
- modelContext.delete(trip)
124
- try modelContext.delete(model: Trip.self, where: #Predicate { $0.isFavorite == false })
125
-
126
- // TRANSACTION (atomic)
127
- try modelContext.transaction {
128
- modelContext.insert(trip); trip.isFavorite = true
188
+ // Create
189
+ let fern = Plant(commonName: "Boston fern")
190
+ context.insert(fern)
191
+ try context.save() // or let autosave handle it
192
+
193
+ // Read
194
+ let overdue = try context.fetch(FetchDescriptor<Plant>(
195
+ predicate: #Predicate { $0.needsRepotting },
196
+ sortBy: [SortDescriptor(\.commonName)]
197
+ ))
198
+
199
+ // Update: assign; autosave or an explicit save persists it
200
+ fern.needsRepotting = true
201
+
202
+ // Delete one, or many by predicate
203
+ context.delete(fern)
204
+ try context.delete(model: CareEvent.self, where: #Predicate { $0.kind == "mist" })
205
+
206
+ // Group changes so they commit together or not at all
207
+ try context.transaction {
208
+ context.insert(Plant(commonName: "Pothos"))
209
+ context.insert(Plant(commonName: "Monstera"))
129
210
  }
130
211
  ```
131
212
 
132
- ## `@Query` in SwiftUI
213
+ ## @Query in views
133
214
 
134
215
  ```swift
135
- struct TripListView: View {
136
- @Query(filter: #Predicate<Trip> { $0.isFavorite == true },
137
- sort: \.startDate, order: .reverse)
138
- private var favorites: [Trip]
216
+ struct RepotList: View {
217
+ @Query(filter: #Predicate<Plant> { $0.needsRepotting },
218
+ sort: \.acquiredOn, order: .reverse)
219
+ private var plants: [Plant]
139
220
 
140
- var body: some View { List(favorites) { trip in Text(trip.name) } }
221
+ var body: some View {
222
+ List(plants) { Text($0.commonName) }
223
+ }
141
224
  }
225
+ ```
142
226
 
143
- // Dynamic query via init
144
- struct SearchView: View {
145
- @Query private var trips: [Trip]
146
- init(search: String) {
147
- _trips = Query(filter: #Predicate<Trip> { trip in
148
- search.isEmpty || trip.name.localizedStandardContains(search)
149
- }, sort: [SortDescriptor(\.name)])
227
+ When the filter depends on input, build the query in `init`:
228
+
229
+ ```swift
230
+ struct PlantSearchResults: View {
231
+ @Query private var plants: [Plant]
232
+
233
+ init(term: String) {
234
+ _plants = Query(
235
+ filter: #Predicate<Plant> {
236
+ term.isEmpty || $0.commonName.localizedStandardContains(term)
237
+ },
238
+ sort: \.commonName
239
+ )
150
240
  }
151
- var body: some View { List(trips) { trip in Text(trip.name) } }
241
+
242
+ var body: some View { List(plants) { Text($0.commonName) } }
152
243
  }
244
+ ```
153
245
 
154
- // FetchDescriptor query
155
- struct RecentView: View {
156
- static var desc: FetchDescriptor<Trip> {
157
- var d = FetchDescriptor<Trip>(sortBy: [SortDescriptor(\.startDate)])
158
- d.fetchLimit = 5; return d
246
+ A static `FetchDescriptor` works too, for example to cap the count:
247
+
248
+ ```swift
249
+ struct NewestPlants: View {
250
+ static var newestFive: FetchDescriptor<Plant> {
251
+ var d = FetchDescriptor<Plant>(sortBy: [SortDescriptor(\.acquiredOn, order: .reverse)])
252
+ d.fetchLimit = 5
253
+ return d
159
254
  }
160
- @Query(RecentView.desc) private var recent: [Trip]
161
- var body: some View { List(recent) { trip in Text(trip.name) } }
255
+ @Query(NewestPlants.newestFive) private var plants: [Plant]
256
+ var body: some View { List(plants) { Text($0.commonName) } }
162
257
  }
163
258
  ```
164
259
 
165
- ## #Predicate
260
+ All `@Query` initializers, error reporting, sectioning and dynamic sorting:
261
+ [references/swiftdata-queries.md](references/swiftdata-queries.md).
262
+
263
+ ## Writing predicates
166
264
 
167
265
  ```swift
168
- #Predicate<Trip> { $0.destination.localizedStandardContains("paris") } // String
169
- #Predicate<Trip> { $0.startDate > Date.now } // Date
170
- #Predicate<Trip> { $0.isFavorite && $0.destination != "Unknown" } // Compound
171
- #Predicate<Trip> { $0.accommodation?.name != nil } // Optional
172
- #Predicate<Trip> { $0.tags.contains { $0.name == "adventure" } } // Collection
266
+ let term = "fern"
267
+ let cutoff = Calendar.current.date(byAdding: .month, value: -6, to: .now) ?? .distantPast
268
+
269
+ let byName = #Predicate<Plant> { $0.commonName.localizedStandardContains(term) }
270
+ let recent = #Predicate<Plant> { $0.acquiredOn > cutoff }
271
+ let combined = #Predicate<Plant> { $0.needsRepotting && $0.commonName != "" }
272
+ let inBed = #Predicate<Plant> { $0.bed?.label != nil }
273
+ let misted = #Predicate<Plant> { plant in
274
+ plant.careEvents.contains { $0.kind == "mist" }
275
+ }
173
276
  ```
174
277
 
175
- Supported: `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`, `!`, `contains()`, `allSatisfy()`, `filter()`, `starts(with:)`, `localizedStandardContains()`, `caseInsensitiveCompare()`, arithmetic, conditional expressions, optional chaining and binding, nil coalescing, type casting. **Avoid**: loops, nested declarations, mutations, and arbitrary unsupported method calls.
278
+ Supported: `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`, `!`, `contains()`,
279
+ `allSatisfy()`, `filter()`, `starts(with:)`, `localizedStandardContains()`,
280
+ `caseInsensitiveCompare()`, arithmetic, conditional (ternary) expressions,
281
+ optional chaining and binding, `??`, and type casts.
282
+
283
+ Keep out of predicates: loops, local declarations, mutation and any method not
284
+ on that list. Such code often compiles and then fails at runtime. The crash
285
+ table and a debugging routine are in
286
+ [references/predicate-pitfalls.md](references/predicate-pitfalls.md).
176
287
 
177
288
  ## FetchDescriptor
178
289
 
179
290
  ```swift
180
- var d = FetchDescriptor<Trip>(predicate: ..., sortBy: [...])
181
- d.fetchLimit = 20; d.fetchOffset = 0
182
- d.includePendingChanges = true
183
- d.propertiesToFetch = [\.name, \.startDate]
184
- d.relationshipKeyPathsForPrefetching = [\.accommodation]
185
- let trips = try modelContext.fetch(d)
186
- let count = try modelContext.fetchCount(d)
187
- let ids = try modelContext.fetchIdentifiers(d)
188
- try modelContext.enumerate(d, batchSize: 1000) { trip in trip.isProcessed = true }
291
+ var descriptor = FetchDescriptor<Plant>(sortBy: [SortDescriptor(\.commonName)])
292
+ descriptor.fetchLimit = 50
293
+ descriptor.fetchOffset = 100
294
+ descriptor.includePendingChanges = true // the default
295
+ descriptor.propertiesToFetch = [\.commonName]
296
+ descriptor.relationshipKeyPathsForPrefetching = [\.careEvents]
297
+
298
+ let page = try context.fetch(descriptor)
299
+ let total = try context.fetchCount(descriptor)
300
+ let ids = try context.fetchIdentifiers(descriptor)
301
+
302
+ let sweep = FetchDescriptor<Plant>()
303
+ try context.enumerate(sweep, batchSize: 1_000) { plant in
304
+ plant.isHighlighted = false
305
+ }
189
306
  ```
190
307
 
191
- ## Schema Versioning and Migration
308
+ ## Schema versions and migration
309
+
310
+ Each shipped schema is an enum that conforms to `VersionedSchema` and nests its
311
+ own model classes. The plan lists them oldest first and the stages between them.
192
312
 
193
313
  ```swift
194
- enum SchemaV1: VersionedSchema {
195
- static var versionIdentifier = Schema.Version(1, 0, 0)
196
- static var models: [any PersistentModel.Type] { [Trip.self] }
197
- @Model class Trip { var name: String; init(name: String) { self.name = name } }
198
- }
314
+ enum GardenSchemaV1: VersionedSchema {
315
+ static let versionIdentifier = Schema.Version(1, 0, 0)
316
+ static var models: [any PersistentModel.Type] { [Plant.self] }
199
317
 
200
- enum SchemaV2: VersionedSchema {
201
- static var versionIdentifier = Schema.Version(2, 0, 0)
202
- static var models: [any PersistentModel.Type] { [Trip.self] }
203
- @Model class Trip {
204
- var name: String; var startDate: Date? // New property
318
+ @Model final class Plant {
319
+ var name: String
205
320
  init(name: String) { self.name = name }
206
321
  }
207
322
  }
208
323
 
209
- enum TripMigrationPlan: SchemaMigrationPlan {
210
- static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] }
211
- static var stages: [MigrationStage] { [migrateV1toV2] }
212
- static let migrateV1toV2 = MigrationStage.lightweight(
213
- fromVersion: SchemaV1.self, toVersion: SchemaV2.self)
324
+ enum GardenSchemaV2: VersionedSchema {
325
+ static let versionIdentifier = Schema.Version(2, 0, 0)
326
+ static var models: [any PersistentModel.Type] { [Plant.self] }
327
+
328
+ @Model final class Plant {
329
+ @Attribute(originalName: "name") var commonName: String
330
+ var sunlight: String = "partial"
331
+ init(commonName: String) { self.commonName = commonName }
332
+ }
333
+ }
334
+
335
+ enum GardenSchemaV3: VersionedSchema {
336
+ static let versionIdentifier = Schema.Version(3, 0, 0)
337
+ static var models: [any PersistentModel.Type] { [Plant.self] }
338
+
339
+ @Model final class Plant {
340
+ var commonName: String
341
+ var sunlight: String = "partial"
342
+ var sunlightHours: Int = 4
343
+ init(commonName: String) { self.commonName = commonName }
344
+ }
214
345
  }
215
346
 
216
- // Custom migration for data transformation
217
- static let migrateV2toV3 = MigrationStage.custom(
218
- fromVersion: SchemaV2.self, toVersion: SchemaV3.self,
219
- willMigrate: nil,
220
- didMigrate: { context in
221
- let trips = try context.fetch(FetchDescriptor<SchemaV3.Trip>())
222
- for trip in trips { trip.displayName = trip.name.capitalized }
223
- try context.save()
224
- })
347
+ enum GardenMigrationPlan: SchemaMigrationPlan {
348
+ static var schemas: [any VersionedSchema.Type] {
349
+ [GardenSchemaV1.self, GardenSchemaV2.self, GardenSchemaV3.self]
350
+ }
351
+ static var stages: [MigrationStage] {
352
+ [
353
+ .lightweight(fromVersion: GardenSchemaV1.self, toVersion: GardenSchemaV2.self),
354
+ .custom(
355
+ fromVersion: GardenSchemaV2.self, toVersion: GardenSchemaV3.self,
356
+ willMigrate: nil,
357
+ didMigrate: { context in
358
+ let plants = try context.fetch(FetchDescriptor<GardenSchemaV3.Plant>())
359
+ for plant in plants {
360
+ plant.sunlightHours = plant.sunlight == "full" ? 8 : 4
361
+ }
362
+ try context.save()
363
+ }
364
+ )
365
+ ]
366
+ }
367
+ }
225
368
  ```
226
369
 
227
- Lightweight handles: adding optional/defaulted properties, renaming (`originalName`), removing properties, adding model types.
370
+ A lightweight stage covers: new optional or defaulted properties, renames
371
+ through `originalName`, removed properties, and new model types. Anything that
372
+ has to compute values goes in a custom stage.
228
373
 
229
- ## Concurrency (`@ModelActor`)
374
+ ## Background work with @ModelActor
230
375
 
231
376
  ```swift
232
377
  @ModelActor
233
- actor DataHandler {
234
- func importTrips(_ records: [TripRecord]) throws {
235
- for r in records {
236
- modelContext.insert(Trip(name: r.name, destination: r.dest,
237
- startDate: r.start, endDate: r.end))
378
+ actor CareImporter {
379
+ func markWatered(_ ids: [PersistentIdentifier]) throws {
380
+ for id in ids {
381
+ guard let plant = self[id, as: Plant.self] else { continue }
382
+ plant.careEvents.append(CareEvent(kind: "water", loggedAt: .now))
238
383
  }
239
- try modelContext.save() // Always save explicitly in @ModelActor
240
- }
241
-
242
- func process(tripID: PersistentIdentifier) throws {
243
- guard let trip = self[tripID, as: Trip.self] else { return }
244
- trip.isProcessed = true; try modelContext.save()
384
+ try modelContext.save()
245
385
  }
246
386
  }
247
387
 
248
- let handler = DataHandler(modelContainer: container)
249
- try await handler.importTrips(records)
388
+ let importer = CareImporter(modelContainer: container)
389
+ try await importer.markWatered(plants.map(\.persistentModelID))
250
390
  ```
251
391
 
252
- **Rules**: `ModelContainer` is `Sendable`. `ModelContext` is NOT -- use on its creating actor. Pass `PersistentIdentifier` (Sendable) across boundaries. Never pass `@Model` objects across actors.
392
+ - The macro gives the actor its own `modelContext`. Autosave does not cover it,
393
+ so every method that writes ends with `try modelContext.save()`.
394
+ - `ModelContainer` is `Sendable`. `ModelContext` is not and stays on the actor
395
+ that made it.
396
+ - Only `PersistentIdentifier` values (from `persistentModelID`) cross actor
397
+ boundaries. Model instances never do.
253
398
 
254
- ## SwiftUI Integration
399
+ ## SwiftUI wiring and previews
255
400
 
256
401
  ```swift
257
402
  @main
258
- struct MyApp: App {
403
+ struct GardenApp: App {
259
404
  var body: some Scene {
260
- WindowGroup { ContentView() }
261
- .modelContainer(for: [Trip.self, LivingAccommodation.self])
405
+ WindowGroup { PlantListView() }
406
+ .modelContainer(for: [Plant.self, CareEvent.self, GardenBed.self])
262
407
  }
263
408
  }
264
409
 
265
- struct DetailView: View {
266
- @Environment(\.modelContext) private var modelContext
267
- let trip: Trip
410
+ struct AddPlantButton: View {
411
+ @Environment(\.modelContext) private var context
268
412
  var body: some View {
269
- Text(trip.name)
270
- Button("Delete") { modelContext.delete(trip) }
413
+ Button("Add") { context.insert(Plant(commonName: "New plant")) }
271
414
  }
272
415
  }
273
416
 
274
- #Preview {
275
- let config = ModelConfiguration(isStoredInMemoryOnly: true)
276
- let container = try! ModelContainer(for: Trip.self, configurations: config)
277
- container.mainContext.insert(Trip(name: "Preview", destination: "London",
278
- startDate: .now, endDate: .now + 86400))
279
- return TripListView().modelContainer(container)
417
+ @MainActor
418
+ func makePreviewContainer() -> ModelContainer {
419
+ do {
420
+ let container = try ModelContainer(
421
+ for: Plant.self,
422
+ configurations: ModelConfiguration(isStoredInMemoryOnly: true)
423
+ )
424
+ container.mainContext.insert(Plant(commonName: "Aloe"))
425
+ return container
426
+ } catch {
427
+ fatalError("Preview container failed: \(error)")
428
+ }
280
429
  }
281
- ```
282
-
283
- ## Common Mistakes
284
-
285
- **1. `@Model` on struct** -- Use class. `@Model` requires reference semantics.
286
-
287
- **2. `@Transient` without default** -- Always provide default: `@Transient var x: Bool = false`.
288
-
289
- **3. Missing .modelContainer** -- `@Query` returns empty without a container on the view hierarchy.
290
-
291
- **4. Passing model objects across actors:**
292
- ```swift
293
- // WRONG: await handler.process(trip: trip)
294
- // CORRECT: await handler.process(tripID: trip.persistentModelID)
295
- ```
296
430
 
297
- **5. ModelContext on wrong actor:**
298
- ```swift
299
- // WRONG: Task.detached { context.fetch(...) }
300
- // CORRECT: Use @ModelActor for background work
301
- ```
302
-
303
- **6. Unsupported #Predicate expressions:**
304
- ```swift
305
- // WRONG: #Predicate<Trip> { $0.name.uppercased() == "PARIS" }
306
- // CORRECT: #Predicate<Trip> { $0.name.localizedStandardContains("paris") }
307
- ```
308
-
309
- **7. Flow control in #Predicate:**
310
- ```swift
311
- // WRONG: #Predicate<Trip> { for tag in $0.tags { ... } }
312
- // CORRECT: #Predicate<Trip> { $0.tags.contains { $0.name == "x" } }
313
- ```
314
-
315
- **8. No save in `@ModelActor`** -- Always call `try modelContext.save()` explicitly.
316
-
317
- **9. ObservableObject with `@Model`** -- Never use `ObservableObject`/`@Published`. `@Model` generates `Observable`. Use `@Query` in views.
318
-
319
- **10. Non-optional relationship without default:**
320
- ```swift
321
- // WRONG: var accommodation: LivingAccommodation // crashes on reconstitution
322
- // CORRECT: var accommodation: LivingAccommodation?
323
- ```
324
-
325
- **11. Cascade without inverse** -- Specify `inverse:` for reliable cascade delete behavior.
326
-
327
- **12. DispatchQueue for background data work:**
328
- ```swift
329
- // WRONG: DispatchQueue.global().async { ModelContext(container).fetch(...) }
330
- // CORRECT: @ModelActor actor Handler { func fetch() throws { ... } }
431
+ #Preview {
432
+ PlantListView().modelContainer(makePreviewContainer())
433
+ }
331
434
  ```
332
435
 
333
- ## Review Checklist
334
-
335
- - [ ] Every `@Model` is a class with a designated initializer
336
- - [ ] All `@Transient` properties have default values
337
- - [ ] Relationships specify `deleteRule` and `inverse`
338
- - [ ] `.modelContainer` attached at scene/root view level
339
- - [ ] `@Query` used for reactive data display in SwiftUI
340
- - [ ] `#Predicate` uses only supported operators
341
- - [ ] Background work uses `@ModelActor`
342
- - [ ] `PersistentIdentifier` used across actor boundaries
343
- - [ ] Schema changes have `VersionedSchema` + `SchemaMigrationPlan`
344
- - [ ] Large data uses `@Attribute(.externalStorage)`
345
- - [ ] CloudKit models avoid uniqueness, use optional relationships, avoid `.deny`, and do not blanket-optionalize scalars
346
- - [ ] CloudKit sync has iCloud + CloudKit, Remote notifications, and production schema rollout checked
347
- - [ ] Explicit `save()` in `@ModelActor` methods
348
- - [ ] Previews use `ModelConfiguration(isStoredInMemoryOnly: true)`
349
- - [ ] `@Model` classes accessed from SwiftUI views are on `@MainActor` via `@ModelActor` or MainActor isolation
436
+ The views that read models get them from the main context: `@Query` and
437
+ `@Environment(\.modelContext)` both run on the main actor. Work done in a
438
+ `@ModelActor` hands results back as identifiers, and the view resolves them on
439
+ the main context. Shared preview containers and `PreviewModifier` traits are in
440
+ [references/swiftdata-advanced.md](references/swiftdata-advanced.md#previews-with-in-memory-stores).
441
+
442
+ ## Mistakes to catch
443
+
444
+ | Wrong | Right |
445
+ |---|---|
446
+ | `@Model struct` | `@Model` needs a class (reference semantics) |
447
+ | `@Transient var x: Int` | `@Transient var x: Int = 0` |
448
+ | No `.modelContainer` above the view | `@Query` returns nothing; attach it at the scene or root |
449
+ | Passing a model to another actor | Pass `persistentModelID` and resolve on the other side |
450
+ | Using a context inside `Task.detached` | Use a `@ModelActor` actor |
451
+ | `DispatchQueue.global()` with a new `ModelContext` | Use a `@ModelActor` actor |
452
+ | `$0.name.uppercased() == ...` in a predicate | `localizedStandardContains` or `caseInsensitiveCompare` |
453
+ | `for` loop inside a predicate | `contains { }` or `allSatisfy { }` |
454
+ | No `save()` in an actor method | Work is lost; save explicitly |
455
+ | Wrapping a model in `ObservableObject` with `@Published` | It is already `Observable`; read it with `@Query` |
456
+ | Non-optional to-one relationship without default | Crashes when the object is loaded back; make it optional |
457
+ | `.cascade` without `inverse:` | Unreliable deletes; declare the inverse |
458
+
459
+ ## Review checklist
460
+
461
+ - [ ] Each `@Model` is a class with a designated initializer.
462
+ - [ ] Each `@Transient` property has a default value.
463
+ - [ ] Relationships state `deleteRule` and `inverse`.
464
+ - [ ] `.modelContainer` is attached at the scene or root view.
465
+ - [ ] Views display data through `@Query`.
466
+ - [ ] Predicates use only supported operations on stored properties.
467
+ - [ ] Background work runs on a `@ModelActor` actor and saves explicitly.
468
+ - [ ] Only `PersistentIdentifier` crosses actor boundaries.
469
+ - [ ] Schema changes ship with a `VersionedSchema` and a `SchemaMigrationPlan`.
470
+ - [ ] Large binary data uses `@Attribute(.externalStorage)`.
471
+ - [ ] CloudKit models: no uniqueness, optional relationships, no `.deny`, and
472
+ scalars not made optional without a reason.
473
+ - [ ] CloudKit sync: iCloud with CloudKit, Remote notifications, and a
474
+ production schema rollout plan are all confirmed.
475
+ - [ ] Previews and tests use `ModelConfiguration(isStoredInMemoryOnly: true)`.
476
+ - [ ] Models shown in views come from the main context (main actor); background
477
+ actors return identifiers or `Sendable` values, not models.
350
478
 
351
479
  ## References
352
480
 
353
- - [references/swiftdata-advanced.md](references/swiftdata-advanced.md) - custom data stores, history tracking, CloudKit, composite attributes, model inheritance, undo/redo, performance
354
- - [references/swiftdata-queries.md](references/swiftdata-queries.md) - `@Query` variants, FetchDescriptor deep dive, sectioned queries, dynamic queries, background fetch
355
- - [references/core-data-coexistence.md](references/core-data-coexistence.md) - standalone Core Data patterns and Core Data to SwiftData migration
356
- - [references/predicate-pitfalls.md](references/predicate-pitfalls.md) - #Predicate runtime crashes, unsupported expressions, safe patterns
357
- - [references/indexing.md](references/indexing.md) - #Index macro, compound indexes, when to index, migration
481
+ - [references/swiftdata-advanced.md](references/swiftdata-advanced.md): custom
482
+ data stores, history tracking, CloudKit details, Core Data strategies, batch
483
+ work and performance, complex predicates, composite attributes, inheritance,
484
+ multiple configurations, undo and redo, previews, save notifications, errors.
485
+ - [references/swiftdata-queries.md](references/swiftdata-queries.md): every
486
+ `@Query` initializer, `FetchDescriptor` in depth, sorting, sections, dynamic
487
+ queries, background fetches, aggregates, `enumerate`.
488
+ - [references/core-data-coexistence.md](references/core-data-coexistence.md):
489
+ running Core Data and SwiftData on one store and moving from one to the other.
490
+ - [references/predicate-pitfalls.md](references/predicate-pitfalls.md): runtime
491
+ predicate failures, what is supported, safe capture, debugging.
492
+ - [references/indexing.md](references/indexing.md): `#Index`, compound indexes,
493
+ when an index pays off, migration and verification.