0Pricing
Swift Academy · Lesson

Lightweight Migrations and CloudKit Sync

Migrating schema changes and enabling CloudKit sync with NSPersistentCloudKitContainer.

Lightweight Migrations and CloudKit Sync is a free Swift Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Migrations?

When you change your Core Data or SwiftData model (add/rename/remove attributes), you must migrate existing stores.

// Without migration, app crashes on launch:
// "The model used to open the store is incompatible with the one used to create the store"

Lightweight Migration in Core Data

Core Data can automatically infer simple migrations (add attribute, rename, remove) without a mapping model.

let desc = NSPersistentStoreDescription(url: storeURL)
desc.shouldMigrateStoreAutomatically = true
desc.shouldInferMappingModelAutomatically = true
container.persistentStoreDescriptions = [desc]

Version History in .xcdatamodeld

Create a new model version in Xcode: Editor → Add Model Version. Set the current version to the new one.

// In MyModel.xcdatamodeld:
// ├── MyModel.xcdatamodel  (v1 - old)
// └── MyModel 2.xcdatamodel (v2 - current)
// Set current version to v2 in File Inspector

SwiftData Schema Versioning

Use VersionedSchema and SchemaMigrationPlan to manage SwiftData model versions.

enum AppSchemaV1: VersionedSchema {
  static var versionIdentifier = Schema.Version(1,0,0)
  static var models: [any PersistentModel.Type] { [ItemV1.self] }
  @Model final class ItemV1 { var name: String; init(name: String) { self.name = name } }
}

SchemaMigrationPlan

Define migration stages between versions in a SchemaMigrationPlan to guide SwiftData through upgrades.

enum AppMigrationPlan: SchemaMigrationPlan {
  static var schemas: [any VersionedSchema.Type] { [AppSchemaV1.self, AppSchemaV2.self] }
  static var stages: [MigrationStage] { [migrateV1toV2] }
  static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: AppSchemaV1.self, toVersion: AppSchemaV2.self)
}

Custom Migration Stage

For complex migrations (data transforms), use a .custom stage with willMigrate and didMigrate closures.

static let migrateV1toV2 = MigrationStage.custom(
  fromVersion: AppSchemaV1.self,
  toVersion: AppSchemaV2.self,
  willMigrate: { context in
    // transform old data
  },
  didMigrate: nil
)

NSPersistentCloudKitContainer

Replace NSPersistentContainer with NSPersistentCloudKitContainer to enable CloudKit sync.

let container = NSPersistentCloudKitContainer(name: "MyModel")
container.loadPersistentStores { _, error in
  if let error { fatalError("\(error)") }
}
container.viewContext.automaticallyMergesChangesFromParent = true

CloudKit Requirements

CloudKit sync requires: signed-in iCloud account, CloudKit entitlement, and a CloudKit container configured in the developer portal.

// Xcode: Signing & Capabilities → + → iCloud → CloudKit
// Enable "Use CloudKit" in Core Data model inspector

SwiftData with CloudKit

Pass a ModelConfiguration with cloudKitDatabase to enable CloudKit sync in SwiftData.

let config = ModelConfiguration(
  schema: schema,
  cloudKitDatabase: .automatic
)
let container = try ModelContainer(for: schema, configurations: config)

Handling Sync Conflicts

Core Data CloudKit resolves conflicts using a last-write-wins policy. Design models to minimize concurrent write contention.

// Best practice: keep models granular
// One attribute per write concern
// Avoid large blobs that frequently change

Testing Migrations

Test migrations on a copy of the production store before shipping to catch issues before users are affected.

// Copy production store to a temp location
// Load it with the new container and verify objects are intact

Migration Monitoring

Listen for Core Data migration notifications to show progress UI during long migrations.

NotificationCenter.default.addObserver(
  forName: .NSPersistentStoreCoordinatorStoresWillChange,
  object: container.persistentStoreCoordinator,
  queue: .main
) { _ in showMigrationProgress() }

Quick Check

Which NSPersistentContainer subclass enables automatic CloudKit sync?

Lesson Recap

Enable lightweight migration with shouldMigrateStoreAutomatically for simple changes. Use VersionedSchema + SchemaMigrationPlan for SwiftData. Enable CloudKit sync by swapping to NSPersistentCloudKitContainer or ModelConfiguration(cloudKitDatabase: .automatic).

Frequently asked questions

Is the “Lightweight Migrations and CloudKit Sync” lesson free?

Yes — the full text of “Lightweight Migrations and CloudKit Sync” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.

What will I learn in “Lightweight Migrations and CloudKit Sync”?

Migrating schema changes and enabling CloudKit sync with NSPersistentCloudKitContainer. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Swift Academy?

No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Lightweight Migrations and CloudKit Sync” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Swift Academy lesson?

Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Core Data Stack: NSPersistentContainer Setup
  2. SwiftData @Model and ModelContext
  3. Relationships and Fetch Descriptors
  4. Lightweight Migrations and CloudKit Sync
← Back to Swift Academy