在 iOS13 中使用 NSPersistentCloudKitContainer 进行 CloudKit 同步

ian*_*anq 10 core-data cloudkit ios13

我正在为我的核心数据应用程序使用 NSPersistentCloudKitContainer。在测试期间,我检查了设备上所做的更改是否在几秒钟内同步到 CloudKit。

但是,当我在设备上禁用 iCloud 然后立即重新启用时,设备上的所有数据都消失了。我检查我的私有数据库中的数据是否仍然存在于 CloudKit 上。CloudKit 上的数据同步回我的设备花了超过 1 天的时间。

这会在用户更换设备并看到他们的数据一开始消失时引起用户的困惑。问题:如何控制 CloudKit 上的数据同步回我的设备的速度?

Pau*_*tin 12

令人沮丧的是,我认为这是“正常”行为,尤其是对于具有大量要同步的关系的大型数据库 - 无法查看进度(向用户显示),也无法加快进度。

NSPersistentCloudKitContainer似乎将每个关系视为一个个体CKRecord,同步仍然受到相同限制的约束(即一次不超过 400 个“请求”),您经常会在控制台中看到这些 .limitExceeded 错误,但几乎没有其他信息(即,如果/何时将重试??)。

我发现这导致数据库需要几天时间才能完全同步,同时数据看起来很混乱且不完整。我有数以千计的多对多关系,当用户从外部文件恢复他们的数据库时,所有这些 CKRecord 都需要重新创建。

我在这里的主要问题是无法查询NSPersistentCloudKitContainer是否有待处理的请求,还有多少尚未同步等,因此您可以将其转发给 UI 中的用户,这样他们就不会继续删除和恢复思考它'失败'。

当您关闭同步时本地数据被删除的一种方法 - 并且可能节省在您重新打开时必须将其全部“重新同步” - 是NSPersistentContainer在它关闭时使用一个,当它打开NSPersistentCloudKitContainer时使用。

NSPersistentCloudKitContainer是 的子类NSPersistentContainer

我目前在我的自定义 PersistenceService 类中的应用程序中执行此操作:

static var useCloudSync = UserDefaults.standard.bool(forKey: "useCloudSync")
static var persistentContainer:  NSPersistentContainer  = {
    let container: NSPersistentContainer?
    if useCloudSync {
        container = NSPersistentCloudKitContainer(name: "MyApp")
    } else {
        container = NSPersistentContainer(name: "MyApp")
        let description = container!.persistentStoreDescriptions.first
        description?.setOption(true as NSNumber,
                               forKey: NSPersistentHistoryTrackingKey)

    }
    container!.loadPersistentStores(completionHandler: { (storeDescription, error) in
    if let error = error as NSError? {
        fatalError("Unresolved error \(error), \(error.userInfo)")
    }
    })
    return container!
}()
Run Code Online (Sandbox Code Playgroud)

这至少会导致在您的应用程序中关闭 iCloud 时本地数据不会受到影响,并且在重新打开时不需要重新同步所有内容。

我认为您还可以查询iOS以查看用户是否在系统设置中关闭了iCloud并在NSPersistentCloudKitContainer删除所有本地数据之前在两者之间切换。

编辑:添加NSPersistentHistoryTrackingKeyas 没有它,切换回NSPersistentContainerfromNSPersistentCloudKitContainer失败。

它在我的应用程序中正常工作。当用户在我的应用程序中重新启用 iCloud 同步(并从 切换NSPersistentContainerNSPersistentCloudKitContainer)时,它会同步自上次同步以来添加/更改的任何内容,这正是我想要的!

编辑 2:这是上述内容的更好实现

从本质上讲,无论用户是否同步到 iCloud,只需.options将容器上的更改为使用NSPersistentCloudKitContainerOptions(containerIdentifier:)nil。似乎根本不需要在 anNSPersistentCloudKitContainer和 an之间切换NSPersistentContainer

static var synciCloudData = {
    return defaults.bool(forKey: Settings.synciCloudData.key)
}()

static var persistentContainer:  NSPersistentContainer  = {
    let container = NSPersistentCloudKitContainer(name: "AppName")
    
    guard let description = container.persistentStoreDescriptions.first else {
        fatalError("Could not retrieve a persistent store description.")
    }
    
    description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
    
    if synciCloudData {
        let cloudKitContainerIdentifier = "iCloud.com.yourID.AppName"
        let options = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier)
        
        description.cloudKitContainerOptions = options
    } else {
        description.cloudKitContainerOptions = nil
    }
            
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    
    return container
}()
Run Code Online (Sandbox Code Playgroud)

最后,您可以看到 iCloud 同步的状态,尽管很粗糙(即,您无法看到是否有任何内容“同步”,只有同步处于挂起/进行中以及它是成功还是失败。话虽如此,这对于我的应用程序中的用例来说已经足够了。

请参阅用户ggruen对本文底部的回复: NSPersistentCloudKitContainer:如何检查数据是否已同步到 CloudKit