agentleFS
fal3fal3/claude-skills-collection/skills/swiftdata-core-data-migrations/SKILL.md

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.

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
  1. Distinctive:

    All released app versions that users can upgrade from.

    line 30
  2. Distinctive:

    Read the requirements and planning framework before choosing a mechanism.

    line 33
  3. Distinctive:

    Create a source-to-destination field and relationship map.

    line 51
  4. Distinctive:

    Never assume a change is lightweight because it looks small in Swift.

    line 60
  5. Very specific:

    Treat every VersionedSchema as a complete immutable snapshot, not a diff.

    line 64
  6. Very specific:

    Use @Attribute(originalName:) when a compatible rename should retain persistent identity.

    line 80
  7. Distinctive:

    Use the compile-checked SwiftData example as syntax guidance, not as a substitute for an on-disk test.

    line 89
  8. Very specific:

    Keep versioned .xcdatamodel snapshots and set the intended current version.

    line 93
  9. Distinctive:

    Prefer separate stores when adding a SwiftData feature beside an existing Core Data application.

    line 105
  10. Distinctive:

    Do not point both frameworks at the same SQLite file merely because SwiftData uses Core Data internally.

    line 108

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 .xcdatamodel versions.
  • 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

RequirementPreferWhy
Existing healthy SwiftData store, iOS 17+SwiftData migrationAvoid unnecessary persistence replacement
Existing Core Data install baseCore Data migrationMature versioned models, mapping models, and controlled migration
Deployment below SwiftData availabilityCore DataSwiftData is unavailable
New, straightforward Swift-native model on supported OS versionsSwiftDataConcise model and migration APIs
Complex entity splitting/merging or established custom policiesCore DataExplicit mapping models and migration policies
Gradual adoption alongside an existing stackSeparate stores plus a deliberate boundaryAvoid two frameworks implicitly owning one file
CloudKit-backed production dataKeep the proven stack unless requirements justify changeLocal 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:

  • willMigrate operates on the source/old schema.
  • didMigrate operates 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 NSManagedObject or 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:

  1. Fresh-store creation directly at the newest schema.
  2. On-disk upgrades from every supported released schema.
  3. 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

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.