Logo for tanaschita.com

How to migrate to a new schema with SwiftData in iOS

Learn how to create a migration plan in SwiftData.

20 Nov 2023 · 4 min read

Whenever we release a new version of our app where we made changes to our SwiftData models, we need to make sure that SwiftData is able to migrate between the old and the new schema to prevent data loss.

Let's directly jump in and how to create a migration plan in SwiftData.

Sponsorship logo
Preparing for a technical iOS job interview
Check out my new book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, Authentication, Core Data, Concurrency with async/await, Security, Automated Testing and more.
LEARN MORE

The basic steps of a migration with SwiftData are:

  1. Use VersionedSchema to create versions of SwiftData models
  2. Use SchemaMigrationPlan to order the created versions
  3. Define a migration stage for each migration - which can be lightweight or custom

Let's look at each step in more detail.

1. Use VersionedSchema to create versions of SwiftData models

If we want to make a change to a SwiftData model, the first thing to do is to create a schema version for the old model. For example:

enum UserSchemaV1: VersionedSchema {
static var versionIdentifier: String? = "V1"
static var models: [any PersistentModel.Type] {
return [User.self]
}
@Model
class User {
@Attribute(.unique) let id: String
let validated: Bool
...
}
}
enum UserSchemaV2: VersionedSchema {
...
@Model
class User {
@Attribute(.unique) let id: String
@Attribute(originalName: "validated") let isValidated: Bool
}
}

The above example shows two schema versions each containing the model classes they define. In the second version, we renamed the validated property to isValidated and marked it with an appropriate attribute. With that in place, SwiftData can migrate those two versions with a lightweight migration.

2. Use SchemaMigrationPlan to order the created versions

After defining a new version, we need to create a migration plan which describes how to handle the migrations from version to version:

enum UsersMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[UserSchemaV1.self, UserSchemaV2.self]
}
}

With schemas, we tell the migration plan in which order the migration should be performed.

3. Define the migration stage for each migration

With that in place, we now can define the migration stage for each migration. A migration stage can be a lightweight or custom.

For a specific set of changes, SwiftData can perform a lightweight migration. Examples of those changes are adding, renaming or deleting entities, attributes or relationships, changing the relationship type and more.

Since we only renamed an attribute in our example and already annotated it, we can use the lightweight migration:

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

When changes exceed the capabilities of a lightweight migration, we need to do a custom migration which can look as follows:

static let migrateV1toV2 = MigrationStage.custom(
fromVersion: UserSchemaV1.self,
toVersion: UserSchemaV2.self,
willMigrate: { context in
// Custom steps before the migration begins
},
didMigrate: { context in
// Custom steps after the migration
}
)

As we can see above, the custom stage provides us with two closures, which we can use to perform an operation before or after the data is migrated.

Sponsorship logo
Preparing for a technical iOS job interview
Check out my new book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, Authentication, Core Data, Concurrency with async/await, Security, Automated Testing and more.
LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

swiftdata

persistence

swiftui

swift

ios

How to get a SwiftData model container and context in SwiftUI

Learn how to setup a SwiftData container and context directly in SwiftUI.

30 Oct 2023 · 4 min read

Latest articles and tips

© 2023 tanaschita.com

Privacy policy

Impressum