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

我的App为何无法正常运行Core Data分段迁移?

Core Data分段迁移未生效的问题排查与修复

针对你的Core Data分段迁移未生效的问题,结合代码逻辑和迁移规则,以下是关键问题点及修复方案:

1. V1→V2阶段错误使用自定义迁移类型

V1到V2属于轻量迁移场景(仅新增dateInTime属性),但代码中误用了NSCustomMigrationStage,这会覆盖Core Data默认的轻量迁移逻辑,导致迁移路径不匹配。正确做法是使用NSLightweightMigrationStage:

// 替换原V1→V2阶段初始化代码
let v1ToV2 = NSLightweightMigrationStage(fromVersionChecksums: [v1Model.versionChecksum])
v1ToV2.label = "V1 to V2: Add dateInTime attribute"

2. V2→V3自定义迁移的上下文与时机错误

你在willMigrateHandler中操作的是目标容器上下文,但此时源存储尚未完成迁移,正确应通过NSStagedMigrationManager获取源上下文处理旧数据。同时建议使用migrateHandler替代willMigrateHandler,确保在迁移阶段内执行数据转换:

v2ToV3.migrateHandler = { migrationManager, currentStage in
    guard let sourceContext = migrationManager.sourceContext else {
        fatalError("Failed to get source context")
    }
    
    sourceContext.performAndWait {
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Book")
        fetchRequest.predicate = NSPredicate(format: "date != nil")
        
        do {
            let fetchedResults = try sourceContext.fetch(fetchRequest)
            
            for book in fetchedResults {
                guard let dateString = book.value(forKey: "date") as? String else {
                    continue // 避免崩溃,可按需调整异常处理逻辑
                }
                let formatter = DateFormatter()
                formatter.dateFormat = "yyyy.MM.dd E"
                formatter.locale = Locale(identifier: "zh_CN") // 明确Locale避免解析失败
                formatter.timeZone = TimeZone.current
                
                if let date = formatter.date(from: dateString) {
                    book.setValue(date, forKey: "dateInTime")
                    book.setValue(nil, forKey: "date")
                }
            }
            try sourceContext.save()
        } catch {
            fatalError("Migration data processing failed: \(error.localizedDescription)")
        }
    }
}

3. 模型版本校验与匹配验证

  • 确认MyDataModel.mom、MyDataModel 2.mom、MyDataModel 3.mom文件存在于MyAppDataModel.momd包内
  • 打印v1Model.versionChecksum等校验值,确保与模型文件的实际校验和一致,避免因校验不匹配导致迁移跳过

修复后的完整代码示例

guard let packageURL = Bundle.main.url(forResource: "MyAppDataModel", withExtension: ".momd") else {
    fatalError("Failed to find file matched with name")
}
let v1ModelURL = packageURL.appending(component: "MyDataModel.mom")
let v2ModelURL = packageURL.appending(component: "MyDataModel 2.mom")
let v3ModelURL = packageURL.appending(component: "MyDataModel 3.mom")

guard let v1Model = NSManagedObjectModel(contentsOf: v1ModelURL),
      let v2Model = NSManagedObjectModel(contentsOf: v2ModelURL),
      let v3Model = NSManagedObjectModel(contentsOf: v3ModelURL) else {
    fatalError("Failed to find model related to the URL")
}

let v1ModelReference = NSManagedObjectModelReference(model: v1Model, versionChecksum: v1Model.versionChecksum)
let v2ModelReference = NSManagedObjectModelReference(model: v2Model, versionChecksum: v2Model.versionChecksum)
let v3ModelReference = NSManagedObjectModelReference(model: v3Model, versionChecksum: v3Model.versionChecksum)

// 修正V1→V2为轻量迁移阶段
let v1ToV2 = NSLightweightMigrationStage(fromVersionChecksums: [v1Model.versionChecksum])
v1ToV2.label = "V1 to V2: Add dateInTime attribute"

let v2ToV3 = NSCustomMigrationStage(migratingFrom: v2ModelReference, to: v3ModelReference)
v2ToV3.label = "V2 to V3: Convert date String to Date and update dateInTime"

// 修正自定义迁移逻辑
v2ToV3.migrateHandler = { migrationManager, currentStage in
    guard let sourceContext = migrationManager.sourceContext else {
        fatalError("Failed to get source context")
    }
    
    sourceContext.performAndWait {
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Book")
        fetchRequest.predicate = NSPredicate(format: "date != nil")
        
        do {
            let fetchedResults = try sourceContext.fetch(fetchRequest)
            
            for book in fetchedResults {
                guard let dateString = book.value(forKey: "date") as? String else {
                    continue
                }
                let formatter = DateFormatter()
                formatter.dateFormat = "yyyy.MM.dd E"
                formatter.locale = Locale(identifier: "zh_CN")
                formatter.timeZone = TimeZone.current
                
                if let date = formatter.date(from: dateString) {
                    book.setValue(date, forKey: "dateInTime")
                    book.setValue(nil, forKey: "date")
                }
            }
            try sourceContext.save()
        } catch {
            fatalError("Migration data processing failed: \(error.localizedDescription)")
        }
    }
}
        
let migrationManager = NSStagedMigrationManager([v1ToV2, v2ToV3])
let persistentContainer = NSPersistentContainer(name: "MyAppDataModel")

guard let storeDescription = persistentContainer.persistentStoreDescriptions.first else {
    fatalError("Failed to get a first description")
}
storeDescription.setOption(migrationManager, forKey: NSPersistentStoreStagedMigrationManagerOptionKey)

persistentContainer.loadPersistentStores { description, error in
    if let error = error {
        fatalError("Failed to load persistent store: \(error.localizedDescription)")
    }
}

额外测试建议

  • 测试时确保设备/模拟器上安装的是V1版本App,再安装新版本触发迁移
  • 若仍未生效,添加启动参数-com.apple.CoreData.MigrationDebug 1开启迁移日志,查看详细迁移过程定位问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 02:30:02