One workaround (from a forum post I'll try to find!) is to create an intermediate model and copy over the data in a complex migration:
enum TripsSchemaV1_0_0: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] { [Trip.self] }
@Model
class Trip {
var name: String
init(name: String) {
self.name = name
}
}
}
enum TripsSchemaV1_1_0: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 1, 0)
static var models: [any PersistentModel.Type] { [Trip.self] }
@Model
class Trip {
var name: String
var title: String? // Migrated in schema v2.0
init(name: String, title: String? = nil) {
self.name = name
self.title = title
}
}
}
enum TripsSchemaV2_0_0: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] { [Trip.self] }
@Model
class Trip {
var title: String
init(title: String) {
self.title = title
}
}
}
Then in your MigrationPlan, move the data:
enum TripsSchemaMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[
TripsSchemaV1_0_0.self,
TripsSchemaV1_1_0.self,
TripsSchemaV2_0_0.self,
]
}
static var stages: [MigrationStage] {
[
v1_0_0_to_v0_1_1_lightweight,
v1_1_0_to_v0_2_0_custom,
]
}
/// Filling in with `nil`, which SwiftData will happily do :)
private static let v1_0_0_to_v0_1_1_lightweight = MigrationStage.lightweight(
fromVersion: TripsSchemaV1_0_0.self,
toVersion: TripsSchemaV1_1_0.self
)
private static let v1_1_0_to_v0_2_0_custom = MigrationStage.custom(
fromVersion: TripsSchemaV1_1_0.self,
toVersion: TripsSchemaV2_0_0.self,
willMigrate: { context in
let trips = try context.fetch(FetchDescriptor<TripsSchemaV1_1_0.Trip>())
// Goal: fill-in the new name.
for trip in trips {
trip.title = trip.name
}
// Assuming autosave is on.
},
didMigrate: nil
)
}
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags: