Core data NSPersistentStoreRemoteChangeNotification未被激发

Core data NSPersistentStoreRemoteChangeNotification未被激发,core-data,cloudkit,ios13,nspersistentcloudkitcontainer,Core Data,Cloudkit,Ios13,Nspersistentcloudkitcontainer,我正在尝试在我的CoreData+CloudKit项目中执行历史记录跟踪,该项目使用NSPersistentCloudKitContainer。我一直在追随苹果的脚步 我想在远程存储更新后执行某些任务。为此,苹果建议在应用程序的签名和功能的后台模式部分启用远程通知 我已经为我的项目启用了历史记录跟踪,如Apple的示例项目所示 // turn on persistent history tracking let description = container.persistent

我正在尝试在我的CoreData+CloudKit项目中执行历史记录跟踪,该项目使用
NSPersistentCloudKitContainer
。我一直在追随苹果的脚步

我想在远程存储更新后执行某些任务。为此,苹果建议在应用程序的签名和功能的后台模式部分启用远程通知

我已经为我的项目启用了历史记录跟踪,如Apple的示例项目所示

    // turn on persistent history tracking
    let description = container.persistentStoreDescriptions.first
    description?.setOption(true as NSNumber,
                           forKey: NSPersistentHistoryTrackingKey)

    // ...
我还注册了我的商店,以监听商店的变化

    // turn on remote change notifications
    let remoteChangeKey = "NSPersistentStoreRemoteChangeNotificationOptionKey"
    description?.setOption(true as NSNumber,
                               forKey: remoteChangeKey)

    // ...
还添加了观察者以侦听
NSPersistentStoreRemoteChangeNotification

但是,没有触发
NSPersistentStoreRemoteChangeNotification
。为了确保我的实现没有错误,我只是在苹果提供的示例代码
@objc func storeRemoteChange(u-notification:notification)
中设置了断点,但我仍然看不到任何通知被触发,也没有断点被激活


我已经了解了在示例项目中完成的标记重复数据消除,并尝试过对其进行测试,但没有任何成功。这是苹果实现中的一个bug还是我缺少了任何必要的设置?

调试OP提到的示例应用程序时,我观察到以下几点:

  • 从XCode版本11.3(11C29)开始,选项键(
    NSPersistentStoreRemoteChangeNotificationPostOptionKey
    )和通知名称(
    .NSPersistentStoreRemoteChange
    )都有SDK常量,这些常量反映在最新下载的示例代码中
  • 示例应用程序在错误的对象上注册远程更改通知,因此它从未收到任何更改通知。根据接受的答案更改发件人可以解决此问题
  • 应用程序UI始终会更新以反映从云收到的更改,但这些更新不是由远程更改通知提示的,而是由应用程序的
    NSFetchedResultsController
    委托使用
    controllerdChangeContent
    回调刷新UI提示的
  • 示例应用程序使用的标准
    NSPersistentCloudKitContainer
    正在将所有云发送的更新自动导入到本地永久存储中,因为persistentStore设置为历史跟踪,viewContext设置为自动更新到最新一代数据,每次导入都会触发UI更新
基于这些观察,我从头开始编写了一个小应用程序,它基于通过指定CoreData、CloudKit和SwiftUI的使用而获得的XCode模板。我以与示例应用程序相同的方式设置其持久容器和视图上下文,并使用SwiftUI的
@FetchRequest
包装器在主视图显示中获取数据。果然,在不使用任何远程更改通知的情况下,我看到了完全相同的远程导入行为,并且每次导入后都会更新UI


然后,我确认,根据接受的答案,如果我正确注册远程更改通知,它们将被接收。它们似乎是在NSPersistentCloudKit中的每个接收和导入操作完成后发送的。不需要观察它们来获取由这些导入启动的本地数据更改的通知。

我猜您是在观察容器而不是商店协调员,请按如下方式添加您的观察者:

    NotificationCenter.default.addObserver(
        self, selector: #selector(type(of: self).storeRemoteChange(_:)),
        name: .NSPersistentStoreRemoteChange, object: container.persistentStoreCoordinator)
注意最后一个参数container.persistentStoreCoordinator

还有一个警告,此通知会出现在所有不同的线程上,因此您要小心并发性。只需在方法中放置5秒睡眠,您将在应用程序启动时看到3个不同的线程调用它。这可能就是为什么在示例中有一个
historyQueue
maxOperationCount
1来处理它


有些通知在
userInfo
中有
NSPersistentHistoryTokenKey
,不知道原因。

我不知道这是否是一个bug。只需下载并运行苹果的示例项目,但永远不会触发
NSPersistentStoreRemoteChangeNotification

我在AppDelegate中为相同的
NSPersistentStoreRemoteChangeNotification
添加了一个观察者,它正在启动

我在AppDelegate中添加了通知观察者,然后只需调用CoreDataStack的
StoreRemoteChange(:)
。此外,标记重复数据消除逻辑工作正常

下面是我在AppDelegate中添加的代码

func应用程序(application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplication.launchOptions键:任意])->Bool{
//视图控制器层次结构在主情节提要中定义。
guard let splitViewController=window?.rootViewController作为UISplitViewController,
将navController=splitViewController.ViewController[splitViewController.ViewController.count-1]设为?UINavigationController,
让topViewController=navController.topViewController{
返回错误
}
//配置splitViewController。
topViewController.navigationItem.leftBarButtonItem=splitViewController.displayModeButtonItem
splitViewController.delegate=self
splitViewController.preferredDisplayMode=.allVisible
//观察核心数据远程更改通知。
NotificationCenter.default.addObserver(
self,selector:#selector(类型(of:self).storeRemoteChange(35;:),
名称:.NSPersistentStoreRemoteChange,对象:nil)
返回真值
}
@objc
func storeRemoteChange(uu通知:通知){
coreDataStack.storeRemoteChange(通知)
}

我能够通过iCloud在我的项目中的两台设备之间可靠地回显核心数据更改。但我已经到了需要了解变革历史的地步。苹果已经很好地描述了在中设置它的步骤

我跟着
// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentCloudKitContainer = {
    /*
     The persistent container for the application. This implementation
     creates and returns a container, having loaded the store for the
     application to it. This property is optional since there are legitimate
     error conditions that could cause the creation of the store to fail.
    */
    let container = NSPersistentCloudKitContainer(name: "yourProjectNameGoesHere")
    
    // turn on persistent history tracking
    // https://developer.apple.com/documentation/coredata/consuming_relevant_store_changes
    let description = container.persistentStoreDescriptions.first
    description?.setOption(true as NSNumber,
                           forKey: NSPersistentHistoryTrackingKey)
    
    // turn on remote change notifications
    let remoteChangeKey = "NSPersistentStoreRemoteChangeNotificationOptionKey"
    description?.setOption(true as NSNumber,
                               forKey: remoteChangeKey)
    
    // this will make background updates from iCloud available to the context.
    container.viewContext.automaticallyMergesChangesFromParent = true
    
    // call this LAST, after the persistentStoreDescriptions configuration.  
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
             
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    
    return container
}()
init() {
    NotificationCenter.default.addObserver(self,
        selector: #selector(fetchChanges),
            name: .NSPersistentStoreRemoteChange,
          object: pc.persistentStoreCoordinator)
}

@objc func fetchChanges(note: Notification) {
    print("Just received a NSPersistentStoreRemoteChange notification")
}