无法find迁移的映射模型 – UIManagedDocument核心数据迁移

我有两个版本的模型Model001.xcdatamodelModel002.xcdatamodel 。 这两个在Model.xcdatamodeld包中。 我也有Model001to002.xcmappingmodel不是Model.xcdatamodeld一部分。 我检查:xcmappingmodel和xcdatamodeld都被复制到.app包中。

我的托pipe对象上下文是这样初始化的:

  NSURL *documentModel = [bundle URLForResource:@"Model" withExtension:@"momd"]; managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:documentModel]; return managedObjectModel; 

我还在我重写的initWithFileURL:上设置了这些属性initWithFileURL:在我的UIManagedObject子类中。

  NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:self.persistentStoreOptions]; [options setObject:@YES forKey:NSMigratePersistentStoresAutomaticallyOption]; [options setObject:@YES forKey:NSInferMappingModelAutomaticallyOption]; self.persistentStoreOptions = [options copy]; 

但是,当我尝试打开文档时,出现以下错误: Can't find mapping model for migration

– 更新 –

即使我做了手动迁移

  [NSMappingModel mappingModelFromBundles:@[[NSBundle mainBundle]] forSourceModel:sourceObjectModel destinationModel:self.managedObjectModel]; 

这返回零。 虽然我仔细检查了Model001to002.cdm是在应用程序包中。 它必须在应用程序包正确?

使用映射模型的“陷阱”是在创build映射之后不允许对模型进行任何更改。 如果你这样做,你也会得到这个错误。

在创build映射模型之后,不允许对源/目标模型进行任何更改。

如果你做了一些改变,

  • mappingModelFromBundles:forSourceModel:destinationModel:将无法find映射模型文件
  • addPersistentStoreWithType:configuration:URL:options:error: with {NSInferMappingModelAutomaticallyOption: @NO}将报告错误“找不到映射模型进行迁移”
  • migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:将报告一个错误“映射和源/目标模型之间不匹配”

因此,只需重新创build映射模型,并复制您在旧版本中所做的每一项更改。

好了,通过从Xcode中删除所有核心数据文件,读取它们并再次设置映射模型的源和目标来解决问题。

该死的你Xcode!

如果您的testing设备的商店来自不再存在的数据模型版本,则会发生这种情况。

例如,我有数据模型版本7,然后我做了数据模型版本8.我做了一个映射模型从7到8.然后我跑在我的testing设备上,一切都很开心。

然后我做了一些更改为8。

要实现的是在Core Data中,每个模型都有一个哈希标识符,系统通过获取xcdatamodel文件的校验和来创build它。 所以即使你做了一个小的改动,即使你没有创build一个新的版本,也会把它看作是一个不同的版本 。 这些版本的标识符是NSStoreModelVersionHashes (请参阅文档)。

换句话说,我结束了:

 Data Model 7 (release) - 0plcXXRN7XHKl5CcF+fwriFmUpON3ZtcI/AfK748aWc= Data Model 8 (beta) - qeN1Ym3TkWN1G6dU9RfX6Kd2ccEvcDVWHpd3LpLgboI= Data Model 8 (release) - EqtMzvRnVZWkXwBHu4VeVGy8UyoOe+bi67KC79kphlQ= 

而不是制作版本9,并在数据模型历史中保存原始版本8,我只是更新了8,认为自动迁移可以照顾我。 那么它不可能,而且我也不能在两者之间做一个映射,因为旧的(testing版)8已经没有了。

我是这样做的,因为它是一个中间的内部构build(而不是释放),所以这不是什么大问题,但它确实让我一个循环!

如果不是内部版本,我需要做这个工作,我可以回到(testing版)提交,并提取8(testing版)的xcdatamodel文件,将(版本)版本重命名为9,然后将其粘贴进入发布版本,并build立一个8和9之间的映射模型。

但是,由于这只是一个内部testing版本,我们只是删除并重新安装在testing设备上的应用程序。 我们确实证实,从7(发布)到8(发布)时,迁移进行得很顺利。

TL; DR

至less从Xcode 8/9开始,打开映射模型,然后从编辑器菜单中select刷新数据模型 。 通常看起来你需要重新启动Xcode。 如果不这样做,您可以尝试在模型编辑器的底部重新select目的地。

更多提示

在应用程序构build中分配模型之后绝对不要更改模型。

对于这个例子,假设您已经发布了数据模型1(DM1)并正在迁移到DM2。 如果您将DM2设置为活动版本,则运行您的应用程序,迁移将在您的持久性存储上运行。 如果你再对DM2进行更改,请运行你的应用程序…轰隆!

问题是您的商店已经迁移到“DM2”,但商店中的数据不再适合模型。 而且,我们不能再从DM2迁移到DM2。

这似乎是一个明显的解决scheme,继续创buildDM3。 尽pipe在开发过程中尽量减less模型和迁移的数量通常是一个好主意。

所以…现在你有一个持久的商店已经迁移到一个已经停止的DM2。 你如何再次testing迁移? 您可以恢复您的应用程序,并与DM1生成一些数据,但我更喜欢使用备份

创build备份

在使用DM2运行应用程序之前,您可以复制现有的存储(使用DM1)以用于以后的testing迁移。 在macOS上,您可以轻松地手动执行此操作。 下面的代码也应该做的伎俩。 通常情况下,你不想发布这个,而是你可以把它放在正常的CD栈打开之前的某个地方,运行应用程序,然后停止应用程序(也许放置一个断点,然后通过Xcode结束运行)。

 let fm = FileManager.default let url = // The store URL you would use in ↓ // try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil) let dir = url.deleteLastPathComponent().appendingPathComponent("Backup", isDirectory: true).appendingPathComponent("DM1", isDirectory: true) print("Saving DB backup for DM1") if !fm.fileExists(atPath: dir.path) { do { // Create a directory try fm.createDirectory(at: dir, withIntermediateDirectories: true, attributes: nil) let backupURL = dir.appendingPathComponent(url.lastPathComponent) try fm.copyItem(at: url, to: backupURL) } catch { print("Failed to save DB backup") } } 

哎呀,我需要再做一个改变…

如果你运行迁移到DM2然后意识到你需要做另一个改变,你将要重新testing你从DM1 – > DM2的迁移。 这是备份来的地方。

同样的方式你做了备份,运行这个代码。

 let fm = FileManager.default let url = // The store URL you would use to add the store let dir = url.deleteLastPathComponent().appendingPathComponent("Backup", isDirectory: true).appendingPathComponent("DM1", isDirectory: true) let backupURL = dir.appendingPathComponent(url.lastPathComponent) if fm.fileExists(atPath: backupURL.path) { do { fm.removeItem(at: url.path) try fm.copyItem(at: backupURL, to: url) } catch { print("Failed to restore DB backup") } } 

你现在有一个恢复的DM1商店,并已经对DM2进行了修改。 如果运行应用程序,迁移可能会成功,但不会使用您的自定义映射模型。

请记住,如果您正在使用自定义映射,那么在映射模型工作之前,您仍然需要使用“ 刷新数据模型”技术。