swiftdata-core-data-migrations
Skill18 starsChanged 57 days agoMITView on GitHub
Design, implement, diagnose, and test production schema migrations for SwiftData and Core Data, including VersionedSchema snapshots, SchemaMigrationPlan stages, lightweight and custom Core Data mappings, gradual adoption, CloudKit constraints, and forward-recovery planning. Use when a persistent model changes, an existing store must upgrade without data loss, a team is choosing between SwiftData and Core Data for migration requirements, or copied production stores need validation. Do not use for ordinary fetch/query code, transient in-memory model transformations, unrelated file-format migrations, or advice that assumes Core Data should be replaced merely because SwiftData exists.
In this repository14 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- app-intents-widgetsSkill
- Cross-Platform App Development SkillSkill
- iOS Accessibility SkillSkill
- iOS Animation Graphics SkillSkill
- iphone-duo-designSkill
- Memory Leak Diagnosis SkillSkill
- swift-concurrency-migrationSkill
- Swift Modern Architecture SkillSkill
- Swift Performance Optimization SkillSkill
- SpeechAnalyzer Framework ExpertSkill
- SwiftUI Programming SkillSkill
- Swift Unit Testing SkillSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 7 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈7
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 30
All released app versions that users can upgrade from.
- Distinctive: line 33
Read the requirements and planning framework before choosing a mechanism.
- Distinctive: line 51
Create a source-to-destination field and relationship map.
- Distinctive: line 60
Never assume a change is lightweight because it looks small in Swift.
- Very specific: line 64
Treat every VersionedSchema as a complete immutable snapshot, not a diff.
- Very specific: line 80
Use @Attribute(originalName:) when a compatible rename should retain persistent identity.
- Distinctive: line 89
Use the compile-checked SwiftData example as syntax guidance, not as a substitute for an on-disk test.
- Very specific: line 93
Keep versioned .xcdatamodel snapshots and set the intended current version.
- Distinctive: line 105
Prefer separate stores when adding a SwiftData feature beside an existing Core Data application.
- Distinctive: line 108
Do not point both frameworks at the same SQLite file merely because SwiftData uses Core Data internally.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
SwiftData and Core Data Migrations
Protect the user's existing store first. Select the persistence and migration mechanism from requirements and the installed base, not from framework novelty.
Inspect before designing
Identify every supported source version, store configuration, target, and sync mode before editing models.
xcodebuild -version
xcrun swiftc --version
xcodebuild -showBuildSettings -project App.xcodeproj -target App \
| rg 'IPHONEOS_DEPLOYMENT_TARGET|MACOSX_DEPLOYMENT_TARGET|SWIFT_VERSION'
rg --files | rg '\.(xcdatamodel|xcdatamodeld|mom|momd)$|\.entitlements$|\.storekit$'
rg -n 'VersionedSchema|SchemaMigrationPlan|MigrationStage|ModelContainer|NSPersistent(Container|CloudKitContainer)|NSMigrationManager'Record:
- Current and historical SwiftData schemas or
.xcdatamodelversions. - Persistent-store URLs, configurations, App Group use, and SQLite sidecars.
- Minimum OS for the app, extensions, tests, and packages.
- CloudKit container/environment and whether production schema is deployed.
- Store size, relationship cardinality, uniqueness rules, and required invariants.
- All released app versions that users can upgrade from.
- Whether rollback means app-binary rollback, store restoration, or forward repair.
Read the requirements and planning framework before choosing a mechanism.
Choose from requirements
| Requirement | Prefer | Why |
| Existing healthy SwiftData store, iOS 17+ | SwiftData migration | Avoid unnecessary persistence replacement |
| Existing Core Data install base | Core Data migration | Mature versioned models, mapping models, and controlled migration |
| Deployment below SwiftData availability | Core Data | SwiftData is unavailable |
| New, straightforward Swift-native model on supported OS versions | SwiftData | Concise model and migration APIs |
| Complex entity splitting/merging or established custom policies | Core Data | Explicit mapping models and migration policies |
| Gradual adoption alongside an existing stack | Separate stores plus a deliberate boundary | Avoid two frameworks implicitly owning one file |
| CloudKit-backed production data | Keep the proven stack unless requirements justify change | Local and cloud schema evolution both constrain rollout |
Core Data is not obsolete. It remains appropriate for existing applications, older deployment targets, advanced mappings, multiple configurations, and teams that need its controls. Do not propose a SwiftData rewrite without a measured product benefit and an explicit store-conversion plan.
Classify the change
Create a source-to-destination field and relationship map. For each change, decide whether it is:
- additive and inferable;
- a rename with persistent identity preserved;
- a transform requiring old values;
- a relationship or entity reshape;
- a constraint change requiring cleanup first;
- incompatible with an older binary or CloudKit schema.
Never assume a change is lightweight because it looks small in Swift. Prove compatibility using the framework's model/schema metadata and an on-disk upgrade test.
Implement SwiftData migrations
Treat every VersionedSchema as a complete immutable snapshot, not a diff. Include every persistent model and its historical property types, relationships, attributes, and defaults. Do not edit a schema version after releasing it.
List versions in order in one SchemaMigrationPlan, and provide an explicit stage for every supported hop:
enum AppMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[SchemaV1.self, SchemaV2.self]
}
static var stages: [MigrationStage] {
[.lightweight(fromVersion: SchemaV1.self, toVersion: SchemaV2.self)]
}
}Use @Attribute(originalName:) when a compatible rename should retain persistent identity. Use a custom stage only when data preparation or post-migration work is required.
The custom-stage contexts expose different schemas:
willMigrateoperates on the source/old schema.didMigrateoperates on the destination/new schema.
Do not cast or fetch the other version in either closure. If a transform needs facts from both shapes, add an intermediate schema containing temporary transport fields, populate them while the old shape exists, then consume/remove them in a later hop. Batch large transforms and save checkpoints only when the framework's migration semantics make that safe.
Use the compile-checked SwiftData example as syntax guidance, not as a substitute for an on-disk test.
Implement Core Data migrations
Keep versioned .xcdatamodel snapshots and set the intended current version. Prefer inferred lightweight migration only when Core Data can infer a valid mapping. Preserve rename identity with the model editor's renaming identifier.
For type conversion, entity split/merge, relationship reshape, or value synthesis, create a source-to-destination mapping model and use NSEntityMigrationPolicy where expressions are insufficient. Test every progressive hop; do not assume one inferred jump covers skipped releases.
shouldMigrateStoreAutomatically and shouldInferMappingModelAutomatically opt into automatic/inferred behavior. They do not turn an incompatible change into a lightweight migration. See the Core Data configuration example.
For explicit migration, migrate to a new destination URL, validate it, and replace the original store using a coordinated, recoverable procedure. Never copy a live SQLite main file while its -wal contains uncheckpointed data.
Coexist or adopt gradually
Keep ownership unambiguous:
- Prefer separate stores when adding a SwiftData feature beside an existing Core Data application.
- Exchange stable IDs and Sendable value snapshots, not
NSManagedObjector live SwiftData model instances. - Choose one writer for duplicated data and make synchronization idempotent.
- Do not point both frameworks at the same SQLite file merely because SwiftData uses Core Data internally.
- Define how deletion, ordering, conflicts, and partial conversion behave before dual-running.
- Retire the old path only after upgrade telemetry and copied-store tests show conversion is safe.
Treat CloudKit and rollback as separate constraints
A local store migration does not automatically migrate the CloudKit production schema. Keep cloud changes additive and compatible with old clients during staged rollout. Validate supported model shapes and migration behavior for NSPersistentCloudKitContainer or SwiftData CloudKit in a development container, then test the production environment before release.
Assume an older binary may be unable to open a newly migrated local store. Cloud records already uploaded under a new schema also cannot be made old-client-compatible by restoring one local file. Plan forward recovery, feature flags, export/backup where appropriate, and a minimum-supported-client policy. Read CloudKit, rollback, and migration testing.
Verify with real stores
Maintain three mandatory test lanes:
- Fresh-store creation directly at the newest schema.
- On-disk upgrades from every supported released schema.
- Sanitized copies of production-representative stores at realistic size.
Assert row counts, stable IDs, required values, relationships, uniqueness, ordering, and application-level invariants. Reopen the migrated store in a new container/process. Test low disk space and an injected migration failure where feasible. Never delete a failed user store as automatic recovery.
OS 27 beta boundary
Use stable Xcode 26 APIs for the primary plan and compiled examples. If an OS 27-cycle beta introduces a migration API, label it beta, isolate it in a beta-only source path, compile it with that beta SDK, and preserve the stable SwiftData/Core Data path. Runtime #available cannot make symbols from an unavailable SDK compile.
Resources
- Requirements and planning framework
- CloudKit, rollback, and migration testing
- SwiftData versioned-schema example
- Core Data migration configuration example
- Positive and negative prompt scenarios
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

