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

SwiftData轻量迁移添加带默认值非可选关联属性崩溃求助

SwiftData轻量迁移中添加非可选关联属性的问题与解决方案

问题原因解析

你遇到的错误entity=Car, attribute=engine, reason=Validation error missing attribute values on mandatory destination relationship,核心原因是SwiftData轻量迁移无法自动为自定义关联类型的非可选属性生成默认实例。

轻量迁移仅能处理有限的场景,包括:

  • 添加任意类型的可选属性(原始类型或关联类型)
  • 添加带默认值的原始类型非可选属性(比如var age: Int = 18)
  • 修改属性名称(配合@Attribute(.originalName("oldName")))
  • 添加/移除模型实体等

但对于自定义关联类型(比如你的Engine、示例中的Company这类模型对象)的非可选属性,轻量迁移没办法自动创建对应的实例并完成关联,因此会触发必填属性缺失的验证错误。这种情况下必须通过自定义迁移来实现。

自定义迁移实现示例(给User添加带默认值的Company关联属性)

初始版本模型(V1)

@Model
final class User {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

更新版本模型(V2)

新增非可选的Company关联属性,默认值为Company(name: "Default Corp"):

@Model
final class User {
    var name: String
    var company: Company // 非可选关联属性
    
    init(name: String, company: Company = Company(name: "Default Corp")) {
        self.name = name
        self.company = company
    }
}

@Model
final class Company {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

步骤1:配置模型容器与迁移计划

修改PersistenceController,启用自定义迁移并指定迁移计划:

struct PersistenceController {
    static let shared = PersistenceController()

    let container: ModelContainer

    init() {
        let schema = Schema([
            User.self,
            Company.self
        ])
        let modelConfig = ModelConfiguration(
            schema: schema,
            migrationPlan: MigrationPlan.self
        )
        
        do {
            container = try ModelContainer(for: schema, configurations: [modelConfig])
        } catch {
            fatalError("初始化ModelContainer失败: \(error)")
        }
    }
}

步骤2:定义迁移计划与版本Schema

创建迁移计划,实现从V1到V2的自定义迁移逻辑:

// 迁移计划
enum MigrationPlan: SchemaMigrationPlan {
    static var schemas: [VersionedSchema.Type] {
        [SchemaV1.self, SchemaV2.self]
    }
    
    static var stages: [MigrationStage] {
        [migrateV1ToV2]
    }
    
    // V1到V2的自定义迁移步骤
    static let migrateV1ToV2 = MigrationStage.custom(
        fromVersion: SchemaV1.self,
        toVersion: SchemaV2.self,
        willMigrate: { _ in
            // 迁移前准备操作(可选)
        },
        didMigrate: { context in
            // 核心逻辑:为所有旧User实例创建默认Company并关联
            do {
                let users = try context.fetch(FetchDescriptor<User>())
                for user in users {
                    let defaultCompany = Company(name: "Default Corp")
                    context.insert(defaultCompany)
                    user.company = defaultCompany
                }
                try context.save()
            } catch {
                print("迁移出错: \(error)")
            }
        }
    )
}

// V1版本Schema
enum SchemaV1: VersionedSchema {
    static var version: Schema.Version = .init(1, 0, 0)
    
    static var entities: [any PersistentEntity.Type] {
        [UserV1.self]
    }
    
    @Model
    final class UserV1 {
        var name: String
        
        init(name: String) {
            self.name = name
        }
    }
}

// V2版本Schema
enum SchemaV2: VersionedSchema {
    static var version: Schema.Version = .init(2, 0, 0)
    
    static var entities: [any PersistentEntity.Type] {
        [UserV2.self, CompanyV2.self]
    }
    
    @Model
    final class UserV2 {
        var name: String
        var company: CompanyV2
        
        init(name: String, company: CompanyV2 = CompanyV2(name: "Default Corp")) {
            self.name = name
            self.company = company
        }
    }
    
    @Model
    final class CompanyV2 {
        var name: String
        
        init(name: String) {
            self.name = name
        }
    }
}

关键注意事项

  • 自定义迁移中,旧版本模型(如UserV1)和新版本模型(如UserV2)的同名属性会被自动映射,无需额外配置。
  • 必须在didMigrate闭包中手动创建关联模型实例、插入上下文并完成关联,最后保存上下文。
  • 后续版本迭代时,只需在MigrationPlan中新增对应的MigrationStage和VersionedSchema即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:47:11