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

SwiftData从V1到V2迁移时ModelContainer创建失败(iCloud同步场景)

SwiftData自定义迁移(开启iCloud同步时)崩溃问题

实现SwiftData从V1到V2的自定义迁移后,启动创建ModelContainer时崩溃,报错信息:
Thread 1: Fatal error: Failed to create ModelContainer for Shoe: The operation couldn’t be completed. (SwiftData.SwiftDataError error 1.)

模型变更说明

V1版本的Shoe模型仅包含isDefaultShoe: Bool属性;V2版本新增defaultRunTypes: [RunType]属性(RunType是包含5个枚举值的枚举类型),用于扩展不同跑步类型的默认鞋款功能。

相关代码

ModelContainer创建代码

typealias Shoe = ShoesSchemaV2.Shoe

final class ShoesStore {
    
    static let container = {
        let container: ModelContainer
        
        do {
            container = try ModelContainer(for: Shoe.self, migrationPlan: ShoesMigrationPlan.self)
        } catch {
            fatalError("Failed to create ModelContainer for Shoe: \(error.localizedDescription)")
        }
        
        return container
    }()
}

自定义迁移计划代码

enum ShoesMigrationPlan: SchemaMigrationPlan {
    
    static var schemas: [any VersionedSchema.Type] {
        [ShoesSchemaV1.self, ShoesSchemaV2.self]
    }
    
    static var stages: [MigrationStage] {
        [migrateV1toV2]
    }
    
    static let migrateV1toV2 = MigrationStage.custom(
        fromVersion: ShoesSchemaV1.self,
        toVersion: ShoesSchemaV2.self,
        willMigrate: { context in
            print("Migration: willMigrae")
            
            let v1Shoes = try? context.fetch(FetchDescriptor<ShoesSchemaV1.Shoe>())
            
            for v1Shoe in v1Shoes ?? [] {
                print("Migration: V1 Shoe: \(v1Shoe.brand) \(v1Shoe.model) - isDefaultShoe = \(v1Shoe.isDefaultShoe)")
                
                let v2Shoe = ShoesSchemaV2.Shoe(
                    id: v1Shoe.id,
                    image: v1Shoe.image,
                    brand: v1Shoe.brand,
                    model: v1Shoe.model,
                    nickname: v1Shoe.nickname,
                    lifespanDistance: v1Shoe.lifespanDistance,
                    aquisitionDate: v1Shoe.aquisitionDate,
                    totalDistance: v1Shoe.totalDistance,
                    totalDuration: v1Shoe.totalDuration,
                    lastActivityDate: v1Shoe.lastActivityDate,
                    isRetired: v1Shoe.isRetired,
                    retireDate: v1Shoe.retireDate,
                    isDefaultShoe: v1Shoe.isDefaultShoe,
                    defaultRunTypes: v1Shoe.isDefaultShoe ? [.daily] : [], // 新增属性
                    workouts: v1Shoe.workouts,
                    personalBests: v1Shoe.personalBests,
                    totalRuns: v1Shoe.totalRuns
                )
                
                print("Migration: V2 shoe created: \(v1Shoe.brand) \(v1Shoe.model) - defaultRunTypes: \(v2Shoe.defaultRunTypes.count)")
                
                context.insert(v2Shoe)
                context.delete(v1Shoe)
            }
            
            try? context.save()
        },
        didMigrate: { context in
            print("Migration: didMigrate")
            let v2Shoes = try? context.fetch(FetchDescriptor<ShoesSchemaV2.Shoe>())

            for v2Shoe in v2Shoes ?? [] {
                print("Migration: V2 shoe: \(v2Shoe.brand) \(v2Shoe.model) - defaultRunTypes: \(v2Shoe.defaultRunTypes.count)")
            }
        }
    )
}

迁移现象

控制台输出显示willMigrate方法执行完成,但didMigrate未执行,随后触发崩溃,输出内容示例:

Migration: willMigrate
Migration: V1 Shoe: Adidas Adizero Boston 12 - isDefaultShoe = true
Migration: V2 shoe created: Adidas Adizero Boston 12 - defaultRunTypes: 1
...(其他鞋款输出)
ShoeHealth/ShoesStore.swift:21: Fatal error: Failed to create ModelContainer for Shoe: The operation couldn’t be completed. (SwiftData.SwiftDataError error 1.)

尝试的解决方案

  • 改用轻量迁移(MigrationStage.lightweight)可正常启动,但所有鞋款的defaultRunTypes属性均为空;
  • 关闭SwiftData iCloud同步后,自定义迁移可正常工作。

疑问

  1. 如何在开启iCloud同步的情况下完成自定义迁移?
  2. 使用静态ModelContainer供WidgetKit调用是否会导致该崩溃问题?

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:50:57