SwiftUI同时使用iCloud Documents和CloudKit时fetch请求实体报错问题
错误原因
这个报错本质是你创建的NSFetchRequest对应的Nota实体,和当前执行请求的NSManagedObjectContext绑定的托管对象模型不匹配,系统无法在当前上下文的模型中找到匹配的实体定义,才会提示"fetch request must have an entity"。
你在视图内单独初始化了一套新的NSManagedObjectModel、NSPersistentStoreCoordinator和NSManagedObjectContext,虽然模型文件和全局CloudKit容器用的是同一个本地文件,但二者是完全独立的实例。Xcode自动生成的Nota.fetchRequest()默认关联的是全局CloudKit容器对应的模型,和你视图内新建的上下文没有关联,所以请求找不到对应实体。
解决方案
方案1:复用全局托管对象模型(推荐)
不要单独初始化上下文,直接给全局的NSPersistentCloudKitContainer新增旧iCloud Documents对应的持久化存储即可,同一套模型下实体完全匹配,不会出现找不到实体的问题。
你可以修改AppDelegate的persistentContainer初始化逻辑,额外加载旧存储:
lazy var persistentContainer: NSPersistentCloudKitContainer = { let container = NSPersistentCloudKitContainer(name: "MyTestApp") // 原有CloudKit存储加载逻辑不变 container.loadPersistentStores(completionHandler: { _, error in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) // 新增加载旧iCloud Documents存储 if let oldICloudURL = FileManager.default.url(forUbiquityContainerIdentifier: "iCloud.it.mytestapp.oldcontainer")?.appendingPathComponent("MyTestApp.sqlite") { do { try container.persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: oldICloudURL, options: nil) } catch { print("加载旧iCloud存储失败: \(error)") } } container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy container.viewContext.automaticallyMergesChangesFromParent = true return container }()
之后在视图中直接用环境注入的managedObjectContext执行请求即可,不需要额外初始化上下文。
方案2:手动指定请求对应的实体
如果你确实需要单独初始化上下文读取旧存储,不要用自动生成的Nota.fetchRequest(),手动创建请求并绑定当前上下文的实体:
// 把原有let fetchRequest: NSFetchRequest<Nota> = Nota.fetchRequest()替换为以下代码 let fetchRequest = NSFetchRequest<Nota>(entityName: "Nota") fetchRequest.entity = NSEntityDescription.entity(forEntityName: "Nota", in: context)
注意事项
- 新旧两套存储使用的托管对象模型版本必须完全一致,否则加载旧存储时会触发Core Data迁移错误
- 填写的
forUbiquityContainerIdentifier参数必须和旧版应用使用的iCloud容器标识符完全匹配,否则无法读取到对应的sqlite文件
内容的提问来源于stack exchange,提问作者Valerio
相关产品推荐
相关产品推荐

