如何在CloudKit中订阅公共数据库的更改?

时间:2018-11-06 19:40:11

标签: ios swift database cloudkit

在CloudKit中订阅公共数据库的最佳方法是什么? 我有一张桌子。每个人都有一个名字和一个位置。 位置更改后,将在CloudKit中更新位置。 那部分工作正常。

但是当记录更新时,我无法使其获得通知。

因为我已经研究了可能的选项,所以某些示例将非常有帮助。 我已经研究了将预订保存在数据库中的选项以及CKModifySubscriptionsOperation选项。

当前,我要订阅的代码如下:

let predicate = NSPredicate(format: "TRUEPREDICATE")
let newSubscription = CKQuerySubscription(recordType: "Person", predicate: predicate, options: [.firesOnRecordCreation, .firesOnRecordDeletion, .firesOnRecordUpdate])
let info = CKSubscription.NotificationInfo()
info.shouldSendContentAvailable = true
newSubscription.notificationInfo = info

database.save(newSubscription, completionHandler: {
      (subscription, error) in
      if error != nil {
          print("Error Creating Subscription")
          print(error)
      } else {
          userSettings.set(true, forKey: "subscriptionSaved")
      }
})

有人可以告诉我我的AppDelegate看起来如何吗?

我已将didReceiveRemoteNotification函数添加到我的AppDelegate中。我也叫application.registerForRemoteNotifications()。这是我的didReceiveRemoteNotification函数的样子: 印刷品甚至不适合我。

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    print("Notification!!")

    let notification = CKNotification(fromRemoteNotificationDictionary: userInfo) as? CKDatabaseNotification
    if notification != nil {
        AppData.checkUpdates(finishClosure: {(result) in
            OperationQueue.main.addOperation {
                completionHandler(result)
            }
        })
    }
}

3 个答案:

答案 0 :(得分:1)

我正在使用RxCloudKit库,这是它如何处理查询通知的代码段-

public func applicationDidReceiveRemoteNotification(userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  let dict = userInfo as! [String: NSObject]
  let notification = CKNotification(fromRemoteNotificationDictionary: dict)
        
  switch notification.notificationType {
  case CKNotificationType.query:
    let queryNotification = notification as! CKQueryNotification
    self.delegate.query(notification: queryNotification, fetchCompletionHandler: completionHandler)
...

此方法从func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)调用

在您收到通知之前,您需要执行以下操作-

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    application.registerForRemoteNotifications()        
...

更新Info.plist应包含以下内容-

<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>remote-notification</string>
</array>

答案 1 :(得分:1)

您还可以检查以下几件事:

= 1 =

确保代码中定义的CloudKit容器与您在CloudKit仪表板中访问的容器相同。有时,当我们创建和测试多个容器时,会忽略在Xcode中选择的CloudKit容器。

= 2 =

检查CloudKit仪表板中的 Subscriptions 标签,并确保在启动应用程序时正在创建Person订阅。如果看到它,请尝试在CK仪表板中将其删除,然后再次运行您的应用程序,并确保它再次出现。

= 3 =

在CK仪表盘中检查日志。每当发送推送通知时,它们将显示类型为push的日志条目。如果您在CK仪表盘中更新/添加记录时正在记录它,那么您就知道问题出在设备上。

= 4 =

请记住,推送通知在iOS模拟器中不起作用。您需要一台实际的设备(如果正在制作macOS应用,则需要一台Mac)。

= 5 =

通过广泛的测试,我发现,即使您始终设置alertBody,即使它为空,通知也更可靠。像这样:

let info = CKSubscription.NotificationInfo()
info.shouldSendContentAvailable = true
info.alertBody = "" //This needs to be set or pushes don't always get sent
subscription.notificationInfo = info

= 6 =

对于iOS应用,我的应用委托处理如下通知:

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    //Ask Permission for Notifications
    UNUserNotificationCenter.current().delegate = self
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { authorized, error in
      DispatchQueue.main.async {
        if authorized {
          UIApplication.shared.registerForRemoteNotifications()
        }
      }
    })

    return true
  }

  //MARK: Background & Push Notifications
  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]{
    let dict = userInfo as! [String: NSObject]
    let notification = CKNotification(fromRemoteNotificationDictionary: dict)

    if let sub = notification.subscriptionID{
      print("iOS Notification: \(sub)")
    }
  }

  //After we get permission, register the user push notifications
  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    //Add your CloudKit subscriptions here...
  }

}

如果仅执行后台推送,则不需要获得通知许可,但是对于用户以弹出式通知形式看到的任何内容,必须获得许可。如果您的应用程序不要求该许可,请尝试将其从设备上删除,然后再次使用Xcode构建。

祝你好运! :)

答案 2 :(得分:0)

更新:正如Reinhard在他的评论中所述:实际上,您仍然可以从公共数据库中订阅更改,并将更改手动导入到Core Data中。我仍然不确定如果苹果特别提到了这些差异,那么依靠公共数据库中的订阅是否是一个好主意

原始答案:

我认为接受的答案不能完全回答这里的问题。 简短的答案是,CloudKit公共数据库不像私有数据库那样支持订阅。相反,只能使用轮询机制。 NSPersistentCloudKitContainer自动处理此问题,但很少更新。

enter image description here

WWDC2020上的这次演讲详细解释了这一点,我建议您观看,因为其中提到了其他重要细节,其中公共数据库与私有数据库不同:https://developer.apple.com/wwdc20/10650

在谈话中,他们提到在每次启动应用程序后以及大约30分钟的应用程序使用后都会启动一次拉动。