@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.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,357 +1,493 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: swiftdata
|
|
3
|
-
description: "
|
|
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:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# SwiftData
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
with
|
|
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
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [CloudKit
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
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
|
|
23
|
-
- [
|
|
24
|
-
- [SwiftUI
|
|
25
|
-
- [
|
|
26
|
-
- [Review
|
|
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
|
-
##
|
|
36
|
+
## Defining models
|
|
30
37
|
|
|
31
|
-
|
|
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
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
+
```swift
|
|
102
|
+
@available(iOS 26, *)
|
|
103
|
+
@Model
|
|
104
|
+
final class Succulent: Plant {
|
|
105
|
+
var waterEveryDays: Int = 14
|
|
56
106
|
|
|
57
|
-
|
|
107
|
+
init(commonName: String, waterEveryDays: Int) {
|
|
108
|
+
self.waterEveryDays = waterEveryDays
|
|
109
|
+
super.init(commonName: commonName)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
58
113
|
|
|
59
|
-
|
|
114
|
+
The base class is declared without `final` so it can be subclassed.
|
|
60
115
|
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
67
|
-
let
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
let config = ModelConfiguration(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
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
|
-
|
|
87
|
-
automatic CloudKit sync, but app entitlements still gate sync.
|
|
171
|
+
Schema rules for a synced store:
|
|
88
172
|
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
##
|
|
185
|
+
## Create, read, update, delete
|
|
107
186
|
|
|
108
187
|
```swift
|
|
109
|
-
//
|
|
110
|
-
let
|
|
111
|
-
|
|
112
|
-
try
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
let
|
|
116
|
-
predicate: #Predicate { $0.
|
|
117
|
-
sortBy: [SortDescriptor(\.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
##
|
|
213
|
+
## @Query in views
|
|
133
214
|
|
|
134
215
|
```swift
|
|
135
|
-
struct
|
|
136
|
-
@Query(filter: #Predicate<
|
|
137
|
-
sort: \.
|
|
138
|
-
private var
|
|
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 {
|
|
221
|
+
var body: some View {
|
|
222
|
+
List(plants) { Text($0.commonName) }
|
|
223
|
+
}
|
|
141
224
|
}
|
|
225
|
+
```
|
|
142
226
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
241
|
+
|
|
242
|
+
var body: some View { List(plants) { Text($0.commonName) } }
|
|
152
243
|
}
|
|
244
|
+
```
|
|
153
245
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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(
|
|
161
|
-
var body: some View { List(
|
|
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
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
#Predicate<
|
|
172
|
-
#Predicate<
|
|
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()`,
|
|
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
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
let
|
|
188
|
-
try
|
|
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
|
|
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
|
|
195
|
-
static
|
|
196
|
-
static var models: [any PersistentModel.Type] { [
|
|
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
|
-
|
|
201
|
-
|
|
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
|
|
210
|
-
static
|
|
211
|
-
static var
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
217
|
-
static
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
374
|
+
## Background work with @ModelActor
|
|
230
375
|
|
|
231
376
|
```swift
|
|
232
377
|
@ModelActor
|
|
233
|
-
actor
|
|
234
|
-
func
|
|
235
|
-
for
|
|
236
|
-
|
|
237
|
-
|
|
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()
|
|
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
|
|
249
|
-
try await
|
|
388
|
+
let importer = CareImporter(modelContainer: container)
|
|
389
|
+
try await importer.markWatered(plants.map(\.persistentModelID))
|
|
250
390
|
```
|
|
251
391
|
|
|
252
|
-
|
|
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
|
|
399
|
+
## SwiftUI wiring and previews
|
|
255
400
|
|
|
256
401
|
```swift
|
|
257
402
|
@main
|
|
258
|
-
struct
|
|
403
|
+
struct GardenApp: App {
|
|
259
404
|
var body: some Scene {
|
|
260
|
-
WindowGroup {
|
|
261
|
-
.modelContainer(for: [
|
|
405
|
+
WindowGroup { PlantListView() }
|
|
406
|
+
.modelContainer(for: [Plant.self, CareEvent.self, GardenBed.self])
|
|
262
407
|
}
|
|
263
408
|
}
|
|
264
409
|
|
|
265
|
-
struct
|
|
266
|
-
@Environment(\.modelContext) private var
|
|
267
|
-
let trip: Trip
|
|
410
|
+
struct AddPlantButton: View {
|
|
411
|
+
@Environment(\.modelContext) private var context
|
|
268
412
|
var body: some View {
|
|
269
|
-
|
|
270
|
-
Button("Delete") { modelContext.delete(trip) }
|
|
413
|
+
Button("Add") { context.insert(Plant(commonName: "New plant")) }
|
|
271
414
|
}
|
|
272
415
|
}
|
|
273
416
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
- [references/
|
|
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.
|