SwiftData 迁移忽略自定义 SchemaMigrationPlan

问题描述 投票:0回答:1

我正在尝试迁移我的 Core Data 应用程序以使用

SwiftData
。目标是完全取代 Core Data。

在开发新版本时,我还对数据库结构进行了相当大的更改(添加实体、添加属性、重命名属性、添加关系、将可选属性转换为非可选属性……)。因此我必须使用自定义迁移。在 WWDC 演讲之后,我添加了

SchemaMigrationPlan
以及我的
VersionedSchema
’s

由于某种原因,它甚至没有到达我的自定义迁移。我做错了什么?

控制台甚至不打印自定义迁移中的

print()
语句。

应用程序

@main
struct MyApp: App {
    let container: ModelContainer
    let dataUrl = URL.applicationSupportDirectory.appending(path: "MyApp.sqlite")
    
    init() {
        let finalSchema = Schema([Loan.self, Item.self, Person.self, Location.self])
        
        do {
            container = try ModelContainer(
                for: finalSchema,
                migrationPlan: MyAppMigrationPlan.self,
                configurations: ModelConfiguration(url: dataUrl))
        } catch {
            fatalError("Failed to initialize model container.")
        }
    }

    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
}

PS:我需要为我的 Core Data 文件指向一个自定义 url,因为几年前我重命名了我的应用程序,并且当时没有更改

NSPersistentContainer
的名称。

迁移计划

enum MyAppMigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [SchemaV1.self, SchemaV2Step1.self, SchemaV2Step2.self]
    }
    
    
    // Migration Stages
    static var stages: [MigrationStage] {
        [migrateV1toV2Step1, migrateV1toV2Step2]
    }
    
    
    // v1 to v2 Migrations
    static let migrateV1toV2Step1 = MigrationStage.custom(
        fromVersion: SchemaV1.self,
        toVersion: SchemaV2Step1.self,
        willMigrate: nil,
        didMigrate: { context in
            print("Start migrating Core Data to SwiftData") // This isn’t printed in the Xcode debug console!?

            let loans = try? context.fetch(FetchDescriptor<SchemaV2Step1.Loan>())
            
            loans?.forEach { loan in
                // Mapping old optional attribute to new non-optional attribute
                loan.amount = Decimal(loan.price ?? 0.0)
                
                // doing other stuff
            }
            
            // Save all changes
            try? context.save()
        })

    
    // Adding new attributes and deleting old attributes replaced in step 1.
    static let migrateV1toV2Step2 = MigrationStage.lightweight(
        fromVersion: SchemaV2Step1.self,
        toVersion: SchemaV2Step2.self)
}

模型架构摘录

此架构反映了我旧的核心数据堆栈,是使用 Xcode 的“创建 SwiftData 代码”操作创建的。由于某种原因,Xcode 生成的代码将

price
属性更改为
Double
。核心数据定义使用
Decimal
强硬。

enum SchemaV1: VersionedSchema {
    static var versionIdentifier = Schema.Version(1, 0, 0)
    
    static var models: [any PersistentModel.Type] {
        [Loan.self, Person.self]
    }

    // definition of all the model classes
    @Model
    final class Loan {
        var price: Double? = 0.0
        // …
    }

    final class Person {
        // …
    }
}

除此之外,此模式还引入了一个新的非可选属性

amount
,它将取代
price
:

enum SchemaV2Step1: VersionedSchema {
    static var versionIdentifier = Schema.Version(1, 5, 0)
    
    static var models: [any PersistentModel.Type] {
        [Loan.self, Person.self, Location.self]
    }

    // definition of all the model classes
    @Model
    final class Loan {
        var amount: Decimal = 0.0 // Will replace `price`
        var price: Double? = 0.0
        // …
    }

    final class Person {
        // …
    }

    // …
}

最终的模式主要删除了上面提到的旧的替换属性:

enum SchemaV2Step2: VersionedSchema {
    static var versionIdentifier = Schema.Version(2, 0, 0)
    
    static var models: [any PersistentModel.Type] {
        [Loan.self, Item.self, Person.self, Location.self]
    }

    // definition of all the model classes
    @Model
    final class Loan {
        var amount: Decimal = 0.0
        // …
    }

    final class Person {
        // …
    }

    // …
}

为了简短起见,我不会显示所有模式的所有详细信息。无论如何,问题似乎出在其他地方......

编辑

最初它在迁移过程中崩溃了:

UserInfo={entity=Loan, attribute=amount, reason=Validation error missing attribute values on mandatory destination attribute}

我已经通过为所有非可选值设置默认值来解决这个问题。我认为问题是我的自定义迁移代码仍然没有执行,因此它只是使用所有默认值而不是我的自定义映射。有什么想法吗?

swiftui core-data-migration swift-data
1个回答
0
投票

我找到了解决这个问题的方法。 在传递 Schema 参数以使用自定义配置时,它似乎不会读取您的迁移计划。 (我不明白为什么会发生)

不使用自定义配置

ModelContainer
可以很好地进行迁移。

相反,我们可以将此初始化器用于

ModelContainer

public convenience init(for forTypes: PersistentModel.Type..., migrationPlan: (SchemaMigrationPlan.Type)? = nil, configurations: ModelConfiguration...) throws

示例代码

    let databasePath = URL.documentsDirectory.appending(path: "database.store")

    let configuration = ModelConfiguration(url: databasePath)

    let container = try ModelContainer.init(
      for: ActiveScheme.Item.self,
      migrationPlan: Plan.self,
      configurations: configuration
    )

要检查您的迁移是否已正确加载,

print(modelContainer.migrationPlan)

必须打印 NOT nil。

© www.soinside.com 2019 - 2024. All rights reserved.