如何解决应用程序卸载/重新安装后,CoreData + Cloudkit fetch不会返回任何结果
我正在尝试使用NSPersistentCloudKitContainer
配置CoreData + CloudKit,以自动将数据同步到CloudKit,或从CloudKit自动同步数据。
在the Apple guide之后,建立实体,在Xcode中添加适当的功能,在我的AppDelegate中设置persistentContainer
和saveContext
都很简单。
我正在通过fetch()
进行save()
和NSManagedObjectContext
的呼叫,并且能够保存和获取记录而没有问题。我可以在CloudKit仪表板中看到它们。
但是,如果我从模拟器中卸载应用程序并重建/重新安装,我的fetchRequest
(没有NSPredicate
或没有排序,只是获取所有记录)总是返回一个空列表。我使用的是相同的iCloud帐户,并且尝试了公共数据库范围和私有数据库范围。如果我创建一个新记录,然后重试获取请求,则可以检索该新创建的记录,但不能检索任何旧记录。我100%肯定这些记录仍在CloudKit数据库中,因为我可以在CloudKit Dashboard Web应用程序上看到它们。
我看过Apple's CoreDataCloudKitDemo应用程序,它能够在卸载/重新安装后从CloudKit数据库中获取“发布”实体,因此我知道这是可能的。但是,它使用的是NSFetchedResultsController
,不适用于我的应用程序(我的游戏是SpriteKit游戏)。
我尝试将CoreData + Cloudkit代码复制到全新的Xcode项目中,并且可以在此处重现此问题。这是我的代码供参考:
import UIKit
import CoreData
@main
class AppDelegate: UIResponder,UIApplicationDelegate {
lazy var persistentContainer: NSPersistentContainer = {
// Create a container that can load CloudKit-backed stores
let container = NSPersistentCloudKitContainer(name: "coredatacloudkitexample")
// Enable history tracking and remote notifications
guard let description = container.persistentStoreDescriptions.first else {
fatalError("###\(#function): Failed to retrieve a persistent store description.")
}
description.setOption(true as NSNumber,forKey: NSPersistentHistoryTrackingKey)
description.setOption(true as NSNumber,forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
description.cloudKitContainerOptions?.databaseScope = .public
container.loadPersistentStores(completionHandler: { (_,error) in
guard let error = error as NSError? else { return }
fatalError("###\(#function): Failed to load persistent stores:\(error)")
})
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.transactionAuthor = "nibbler"
// Pin the viewContext to the current generation token and set it to keep itself up to date with local changes.
container.viewContext.automaticallyMergesChangesFromParent = true
do {
try container.viewContext.setQueryGenerationFrom(.current)
} catch {
fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
}
return container
}()
}
// ------
import UIKit
import CoreData
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
let people: [Person]
do {
people = try viewContext.fetch(fetchRequest)
print("---> fetched People from CoreData: \(people)")
// people.isEmpty is ALWAYS true (empty array) on first install of app,even if records exist in table in CloudKit
if people.isEmpty {
let person = Person(context: viewContext)
person.name = "nibbler"
// save the data through managed object context
do {
try viewContext.save()
print("--> created Person in CoreData: \(person)")
} catch {
print("---> failed to save Person: \(error.localizedDescription)")
}
}
} catch {
print("---> error: \(error)")
}
}
}
我想念什么?为什么我只能提取在此应用的安装过程中创建的记录,而不能提取以前的记录?
更新:看来,如果我等待几秒钟,然后尝试重新安装我上午能够从CloudKit数据库。首次启动时,我也可以在控制台中看到大量CoreData+CloudKit
日志消息。这就是我的想法-即使使用NSPersistentCloudKitContainer
时,fetch()
也在读/写本地CoreData存储,然后在后台运行一个单独的进程来镜像和合并本地CoreData记录与CloudKit记录。
这样,我认为我需要以某种方式等待/被通知,本地CoreData和CloudKit记录的这种同步/合并已在首次启动之前完成,然后进行了fetch()
调用,而不是进行{{1} }在应用打开时立即调用。有什么想法吗?
解决方法
您需要使用NSFetchedResultsController
,为什么您认为它不适用于您的应用?
NSFetchedResultsController
监视viewContext
是必要的原因,并且当同步过程下载新对象并将其插入到后台上下文中时,automaticallyMergesChangesFromParent
会将这些对象合并到{{ 1}},并推进生成令牌。调用FRC的委托方法来通知您是否从获取的对象数组中插入,更新或删除了对象,这些对象是上下文中与获取请求实体和谓词相匹配的对象。
您可以尝试的是这样:
- 设置
NSPersistentCloudKitContainerOptions
let id = "iCloud.yourid"
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: id)
description?.cloudKitContainerOptions = options
- 初始化您的CloudKit模式(至少需要一次)
do {
try container.initializeCloudKitSchema()
} catch {
print("ERROR \(error)")
}
编辑:
您可以更改lazy var persistentContainer: NSPersistentContainer
到lazy var persistentContainer: NSPersistentCloudKitContainer
这是@professormeowingtons,您在模拟器上删除该应用程序时不会提及之前的记录,因此我建议您在已配置iCloud帐户的真实设备上尝试该应用程序,这样'将能够向数据库中添加一些记录,然后删除该应用程序,重新安装并获取您输入的所有先前记录。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。