name: swiftdata-expert description: 'Expert guidance on SwiftData: @Model, ModelContainer, ModelContext, FetchDescriptor, relationships, @ModelActor concurrency, CloudKit sync, migrations (VersionedSchema, SchemaMigrationPlan), and caching strategies. Use when defining models, querying data, setting up persistence, debugging SwiftData issues, planning migrations, or reviewing SwiftData code.'
SwiftData
Overview
Use this skill for authoritative guidance on SwiftData (iOS 17+/macOS 14+). Covers model definition, container/context management, querying, relationships, concurrency with @ModelActor, CloudKit integration, schema migrations, and caching patterns. Assumes Swift 6.2 strict concurrency.
Agent behavior contract (follow these rules)
- Always use
@Modelon classes — never hand-writePersistentModelconformance. - Always use
@ModelActorfor background data access — never passModelContextacross actor boundaries. - Pass
PersistentIdentifier(not model instances) across actors — resolve on the target actor's context. - Check
cloudKitDatabaseconfiguration before applying constraints —@Attribute(.unique)and.denydelete rules are forbidden with CloudKit. - Use
static let(notstatic var) forVersionedSchemaandSchemaMigrationPlanprotocol properties — the protocols require only{ get }. - For non-CloudKit cache stores, use the delete-and-recreate pattern on container failure — never migrate disposable cached data.
- For CloudKit stores, always define
VersionedSchema+SchemaMigrationPlan— user data must never be silently deleted. - Use
FetchDescriptorwith#Predicatefor all queries — never use raw NSPredicate or string-based predicates. - Prefer
@Attribute(.externalStorage)for large binary data — never store images or files inline in SQLite. - Consult
docs/SWIFTDATA.mdfor project-specific patterns before writing any SwiftData code.
First 60 seconds (triage template)
- Clarify the goal: new model, querying, relationships, migration, CloudKit, concurrency, or debugging.
- Collect minimal facts:
- Is the store CloudKit-synced or local-only cache?
- Which context module does this belong to?
- Is this a new model or modification to an existing one?
- Branch quickly:
- new model or properties ->
models.md+attributes.md - querying or filtering ->
querying.md - relationships or delete rules ->
relationships.md - background work or concurrency ->
concurrency.md - schema change or migration ->
migrations.md - CloudKit sync issues ->
cloudkit.md - container/context setup ->
containers.md
- new model or properties ->
Common pitfalls -> next best move
ModelContextpassed across actors -> use@ModelActorwith its own context.@Attribute(.unique)on a CloudKit model -> remove it; CloudKit cannot enforce uniqueness.static varonVersionedSchematriggers concurrency warning -> change tostatic let.nonisolated(unsafe)on schema properties -> unnecessary, usestatic letinstead.- Container creation crashes after schema change -> local-only stores should use delete-and-recreate pattern.
- Orphaned
-waland-shmfiles after database deletion -> clean up all three SQLite files. @Transientproperty without default value -> must provide default (SwiftData needs it for fetch materialisation).- Relationship not optional in CloudKit model -> all CloudKit relationships must be optional.
- Missing
@Attribute(originalName:)after rename -> data will be lost; useoriginalNamefor lightweight migration.
Verification checklist
- Confirm
@Modelis applied to all persistence classes. - Confirm CloudKit models have no
@Attribute(.unique), no.denydelete rules, and all-optional relationships. - Confirm local-only models use
cloudKitDatabase: .noneinModelConfiguration. - Confirm
@ModelActoris used for all background data access (not rawModelContext). - Confirm
VersionedSchemaandSchemaMigrationPlanusestatic let(notstatic var). - Confirm non-CloudKit containers use
ModelContainerFactory.makeLocalModelContainer(delete-and-recreate). - Confirm CloudKit containers use
ModelContainerFactory.makeCloudKitModelContainerwith a migration plan. - Confirm all
@Transientnon-optional properties have default values. - Confirm
FetchDescriptoruses typed#Predicate(not string-based predicates).
References
references/_index.md— navigation index with quick links by problemreferences/models.md—@Model, property types,@Transient,PersistentModelreferences/attributes.md—@Attributeoptions (.unique,.externalStorage,.transformable,originalName)references/relationships.md—@Relationship, delete rules, inverse relationships, cardinalityreferences/querying.md—FetchDescriptor,#Predicate,SortDescriptor,@Query, batch operationsreferences/containers.md—ModelContainer,ModelConfiguration,ModelContext, container factory patternsreferences/concurrency.md—@ModelActor,PersistentIdentifier, thread safety, Swift 6references/cloudkit.md— CloudKit sync, constraints, schema limitations, configurationreferences/migrations.md—VersionedSchema,SchemaMigrationPlan,MigrationStage, lightweight vs custom