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同步后,自定义迁移可正常工作。
疑问
- 如何在开启iCloud同步的情况下完成自定义迁移?
- 使用静态
ModelContainer供WidgetKit调用是否会导致该崩溃问题?
内容的提问来源于stack exchange,提问作者Robert Basamac
相关产品推荐
相关产品推荐

