SwiftDataでMigrationPlanが呼び出されない
SwiftDataを使ってスキーマのマイグレーションをする際に,作成したMigrationPlan
が呼び出されない問題が起きました.
この問題の解決方法を書いておきます.
TL;DR
ModelContainer(for: Schema, migrationPlan: (any SchemaMigrationPlan.Type)?, configurations: ModelConfiguration...)
ではなく,
ModelContainer(for: any PersistentModel.Type..., migrationPlan: (any SchemaMigrationPlan.Type)?, configurations: ModelConfiguration...)
を使う必要があった.
環境
- Xcode: 16.2
- Swift: 5
- iOS: 17.0
問題
カスタムマイグレーションが必要なスキーマ変更があったので,以下のようにMigrationPlan
を定義しました.
struct SchemaV0_1_0: VersionedSchema {
static var versionIdentifier = Schema.Version(0, 1, 0)
static var models: [any PersistentModel.Type] = [
// (省略)
]
}
struct SchemaV0_2_0: VersionedSchema {
static var versionIdentifier = Schema.Version(0, 2, 0)
static var models: [any PersistentModel.Type] = [
// (省略)
]
}
enum MigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
print("schemas called")
return [SchemaV0_1_0.self, SchemaV0_2_0.self]
}
static var stages: [MigrationStage] {
print("stages called")
return [migrateV0_1_0toV0_2_0]
}
static var migrateV0_1_0toV0_2_0 = MigrationStage.custom(fromVersion: SchemaV0_1_0.self, toVersion: SchemaV0_2_0.self, willMigrate: { context in
print("willMigrate called")
}, didMigrate: { context in
print("didMigrate called")
})
}
このMigrationPlan
を,ModelContext
の作成で利用するようにしていました.
let schema = Schema(versionedSchema: SchemaV0_2_0.self)
let container = try! ModelContainer(for: schema, migrationPlan: MigrationPlan.self)
let context = ModelContext(container)
しかし,随所に入れているprint()
はどれも呼び出されませんでした.
解決方法
stack overflowの回答にある通りなのですが,上記の例のようにSchema
を引数に取るModelContext
のコンストラクタではMigrationPlan
が呼び出されず,@Model
のクラスを引数に取るコンストラクタなら呼び出されるようです(SwiftDataのバグ?)
ModelContainer(for: Schema, migrationPlan: (any SchemaMigrationPlan.Type)?, configurations: ModelConfiguration...)
→MigrationPlan
が呼び出されないModelContainer(for: any PersistentModel.Type..., migrationPlan: (any SchemaMigrationPlan.Type)?, configurations: ModelConfiguration...)
→MigrationPlan
が呼び出される
次のように変更することで呼び出されるようになりました.
let container = try! ModelContainer(for: YourModel.self, OtherYourModel.self, migrationPlan: MigrationPlan.self)
let context = ModelContext(container)