@mmerterden/multi-agent-pipeline 20.6.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 (326) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +328 -329
  5. package/package.json +2 -2
  6. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  7. package/pipeline/scripts/_notices.mjs +12 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  9. package/pipeline/skills/.skill-manifest.json +106 -106
  10. package/pipeline/skills/shared/README.md +71 -71
  11. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  16. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  17. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  19. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  20. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  21. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  22. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  23. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  24. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  25. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  26. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  27. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  28. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  29. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  30. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  31. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  32. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  33. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  34. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  35. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  37. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  38. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  39. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  40. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  41. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  42. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  43. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  44. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  45. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  46. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  47. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  48. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  49. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  50. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  51. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  53. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  54. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  55. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  56. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  57. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  58. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  59. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  60. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  61. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  62. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  63. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  64. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  65. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  66. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  67. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  68. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  69. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  70. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  71. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  72. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  73. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  74. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  75. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  76. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  77. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  78. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  79. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  81. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  82. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  83. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  84. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  85. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  86. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  87. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  88. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  89. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  90. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  91. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  92. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  93. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  94. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  95. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  96. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  97. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  98. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  99. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  100. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  101. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  102. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  103. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  104. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  105. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  106. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  107. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  108. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  109. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  110. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  111. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  112. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  113. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  114. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  115. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  116. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  117. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  118. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  119. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  120. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  121. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  122. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  123. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  124. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  125. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  126. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  127. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  128. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  129. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  130. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  131. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  132. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  133. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  134. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  135. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  136. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  137. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  138. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  139. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  140. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  141. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  142. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  143. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  144. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  145. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  146. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  147. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  148. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  149. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  150. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  151. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  152. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  153. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  154. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  155. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  156. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  157. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  158. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  159. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  160. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  161. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  162. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  163. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  164. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  165. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  166. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  167. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  168. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  169. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  170. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  171. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  172. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  173. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  174. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  175. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  176. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  177. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  178. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  179. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  180. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  181. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  183. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  184. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  185. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  186. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  187. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  188. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  189. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  190. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  191. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  192. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  193. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  194. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  195. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  196. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  197. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  198. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  199. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  200. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  201. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  202. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  203. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  204. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  205. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  206. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  207. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  208. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  209. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  210. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  211. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  212. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  213. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  214. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  219. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  220. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  221. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  222. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  223. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  224. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  225. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  226. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  227. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  228. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  229. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  230. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  231. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  232. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  233. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  234. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  235. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  236. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  237. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  238. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  239. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  240. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  241. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  242. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  243. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  244. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  245. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  246. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  247. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  248. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  249. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  250. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  251. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  252. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  254. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  255. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  256. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  257. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  258. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  259. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  261. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  262. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  263. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  264. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  265. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  268. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  269. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  270. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  271. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  272. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  273. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  274. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  275. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  276. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  277. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  278. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  279. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  280. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  281. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  284. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  286. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  291. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  294. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  295. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  296. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  297. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  298. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  299. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  300. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  301. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  302. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  303. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  305. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  306. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  309. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  310. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  311. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  312. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  313. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  314. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  315. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  316. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  317. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  318. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  319. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  320. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  321. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  322. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  323. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
  324. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  325. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  326. 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.