Swift CloudKit CoreData 远程更改通知也会触发本地更改

Posted

技术标签:

【中文标题】Swift CloudKit CoreData 远程更改通知也会触发本地更改【英文标题】:Swift CloudKit CoreData Remote Change Notification also firing for local changes 【发布时间】:2020-12-07 21:57:07 【问题描述】:

我有一个带有 Local 存储和 Cloud 存储的 CoreData 设置,只有后者与 CloudKit 同步。同步本身运行良好。

我正在尝试侦听远程更改通知,以便在来自 CloudKit 的远程更改进入时触发设备上的某些进程。我在持久性控制器中设置了 NSPersistentStoreRemoteChangeNotificationOptionKey 键,并设置了相应的观察者监听这个。

观察者正确触发远程更改通知,但是我的问题是 触发保存视图上下文后在设备上发起的任何 CoreData 更改。

如果这是预期的行为,我希望通知本身至少有一些有用的信息,以便我可以区分更改是由设备上的操作触发还是从 CloudKit 同步的。

是我做错了什么,还是有其他简单的方法可以在远程更改出现时获得通知?

持久性控制器

struct PersistenceController 
    static let shared = PersistenceController()
        
    let container: NSPersistentCloudKitContainer
    
    init() 
        container = NSPersistentCloudKitContainer(name: "MyContainerName")
        
        let storeDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
        
        // Local
        let localUrl = storeDirectory.appendingPathComponent("local.store")
        let local = NSPersistentStoreDescription(url: localUrl)
        local.configuration = "Local"
        
        // Cloud
        let cloudUrl = storeDirectory.appendingPathComponent("cloud.store")
        let cloud = NSPersistentStoreDescription(url: cloudUrl)
        cloud.configuration = "Cloud"
        cloud.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "my.container.identifier")
        cloud.setOption(true as NSNumber, forKey: "NSPersistentStoreRemoteChangeNotificationOptionKey")
        
        container.persistentStoreDescriptions = [local, cloud]
        
        container.loadPersistentStores(completionHandler:  (storeDescription, error) in
            if let error = error as NSError? 
                fatalError("Unresolved error \(error), \(error.userInfo)")
            
        )
        
        container.viewContext.automaticallyMergesChangesFromParent = true
        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        
        

应用代理

class AppDelegate: NSObject, UIApplicationDelegate 
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool 
        
        NotificationCenter.default.addObserver(
            self, selector: #selector(type(of: self).storeRemoteChange(_:)),
            name: .NSPersistentStoreRemoteChange,
            object: PersistenceController.shared.container.persistentStoreCoordinator
        )
        return true
    
    
    @objc
    func storeRemoteChange(_ notification: Notification) 
        
        precondition(notification.name == NSNotification.Name.NSPersistentStoreRemoteChange)

        print(notification.userInfo)
        
    
    

打印语句的输出

Optional([AnyHashable("NSStoreUUID"): C2D121212-421B-003F-9819-956411111169, AnyHashable("storeURL"): file:///var/mobile/Containers/Data/Application/3234FH#-DACF-493I-AD24-143E4CDSFE2/Library/Application%20Support/cloud.store])

【问题讨论】:

【参考方案1】:

如果这是预期的行为,我希望通知本身至少有一些有用的信息,以便我可以区分更改是由设备上的操作触发还是从 CloudKit 同步的。

是的,这是预期的行为,因为无论是在设备本地还是从云端更改,这都会记录为您的商店/数据库中的更改。

因此,为了处理来自云端的更改,您可以订阅数据库更改,请阅读here

【讨论】:

嗨@Stamenkovski,谢谢。我已经测试了您推荐的内容并且它有效,但是通知似乎在 CoreData 模型更新到达并生效前几秒钟到达,我假设这是正常的。我想在收到远程通知时触发一些进程,但只有在实际的 CoreData 更改进来之后。您有什么具体的建议吗? 我不能给你很好的解决方案,一种方法是让你自己同步。这意味着您可以在没有 NSPersistentCloudKitContainer 的情况下使用自定义 CKRecord,并且当发生更改时,处理更改并将 CKRecord 保存到您的自定义核心数据对象。 或在保存到核心数据之前格式化数据。

以上是关于Swift CloudKit CoreData 远程更改通知也会触发本地更改的主要内容,如果未能解决你的问题,请参考以下文章

Swift3、CloudKit 和 UITableView 返回空白表

如何在 Swift 中将 Core Data 与 Cloudkit 和许多设备同步

如何在 Swift 中将位置面包屑保存到 CoreData

CoreData与CloudKit同步时将图像保存到CoreData?

从 CloudKit 获取并保存到 CoreData

CloudKit 和 CoreData 默认值