ottijp blog

SwiftDataでMigrationPlanが呼び出されない

  • 2025-01-18

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)

Refs


ottijp
都内でアプリケーションエンジニアをしています
© 2025, ottijp