You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftData迁移至V2版本添加枚举字段触发致命错误

SwiftData V1→V2迁移崩溃问题解决

问题触发原因

迁移时抛出Fatal error: Expected only Arrays for Relationships - EnumJ,是因为SwiftData在解析V2模型时,误将新增的Codable枚举字段识别为Relationship类型,导致关系类型校验失败。

修复步骤

1. 规范枚举类型的Codable实现

显式实现枚举的Codable逻辑,避免SwiftData隐式解析时的类型混淆:

enum EnumJ: String, Codable, CaseIterable {
    case caseA, caseB, caseC

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(rawValue)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let rawValue = try container.decode(String.self)
        guard let validCase = EnumJ(rawValue: rawValue) else {
            throw DecodingError.dataCorruptedError(
                in: container,
                debugDescription: "Invalid EnumJ value: \(rawValue)"
            )
        }
        self = validCase
    }
}

2. 明确V2模型的字段类型声明

在V2的VersionedSchema中,必须显式标记枚举字段的类型,禁止依赖自动推断:

enum SchemaV2: VersionedSchema {
    static var versionIdentifier: Schema.Version = .init(2, 0, 0)
    static var models: [any PersistentModel.Type] = [ModelA.self, ModelB.self]

    @Model
    class ModelA {
        // 修改原有字段类型(示例:V1为String,V2改为Int)
        var oldField: Int
        // 显式声明枚举字段类型
        var newEnumField: EnumJ
        // 修改关系的deleteRule
        @Relationship(deleteRule: .cascade)
        var relatedItems: [ModelB]?

        init(oldField: Int, newEnumField: EnumJ) {
            self.oldField = oldField
            self.newEnumField = newEnumField
        }
    }

    @Model
    class ModelB {
        // 对应修改的字段与关系
        var id: UUID
        @Relationship(inverse: \ModelA.relatedItems)
        var parent: ModelA?

        init(id: UUID) {
            self.id = id
        }
    }
}

3. 修正自定义迁移逻辑

在迁移阶段明确处理字段转换与枚举默认值,避免类型歧义:

struct CustomMigrationV1ToV2: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] = [SchemaV1.self, SchemaV2.self]

    static var stages: [MigrationStage] = [
        .custom(
            fromVersion: SchemaV1.versionIdentifier,
            toVersion: SchemaV2.versionIdentifier,
            willMigrate: { context in
                // 迁移前处理:转换旧字段类型
                let oldModelAFetch = FetchDescriptor<SchemaV1.ModelA>()
                do {
                    let oldModels = try context.fetch(oldModelAFetch)
                    for model in oldModels {
                        // 示例:将V1的String类型oldField转为Int兼容值
                        if let intValue = Int(model.oldField) {
                            model.oldField = String(intValue)
                        } else {
                            model.oldField = "0"
                        }
                    }
                    try context.save()
                } catch {
                    print("Pre-migration error: \(error)")
                }
            },
            didMigrate: { context in
                // 迁移后处理:为新增枚举字段设置默认值
                let newModelAFetch = FetchDescriptor<SchemaV2.ModelA>()
                do {
                    let newModels = try context.fetch(newModelAFetch)
                    for model in newModels {
                        // 若枚举字段为可选类型,补全默认值
                        if model.newEnumField == nil {
                            model.newEnumField = .caseA
                        }
                    }
                    try context.save()
                } catch {
                    print("Post-migration error: \(error)")
                }
            }
        )
    ]
}

4. 验证容器配置

确保ModelContainer正确关联迁移计划,无重复定义:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(
            for: [ModelA.self, ModelB.self],
            migrationPlan: CustomMigrationV1ToV2.self
        )
    }
}

额外注意事项

  • 避免枚举字段名与关系属性名重复,防止SwiftData解析混淆
  • 所有模型变更(字段类型、关系规则)必须在VersionedSchema中显式声明
  • 迁移时确保数据转换的兼容性,避免丢失或损坏原有数据

内容的提问来源于stack exchange,提问作者Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 06:53:20