I'm trying to set up silent push notifications to propagate local notifications across devices using CloudKit and I can't wrap my head around why my app is crashing every time I receive a remote notification.
Everything works just fine until I try to process the notification received. The crash occurs when trying to access the recordID for the CKQueryNotification object inside didReceiveRemoteNotification.
I'm registering for remote notifications inside application didFinishLaunchingWithOptions. And this is the rest of the code:
Inside didRegisterForRemoteNotification, I'm creating a CKSubscription for the record type CD_Task.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let subscription = CKQuerySubscription(recordType: "CD_Task", predicate: NSPredicate(format: "TRUEPREDICATE"), options: .firesOnRecordCreation)
let info = CKSubscription.NotificationInfo()
subscription.notificationInfo = info
CKContainer.default().privateCloudDatabase.save(subscription, completionHandler: { subscription, error in
if error == nil {
// Subscription saved successfully
} else {
// Error occurred, handle it
}
})
}
Inside DidReceiveRemoteNotification, I pass in a CKQueryNotification object to a function to handle the remote notification received from iCloud:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let ckqn = CKQueryNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject]) {
self.iCloudHandleNotification(ckqn)
}
}
Finally, here is the iCloudHandleNotification function code:
func iCloudHandleNotification(_ ckqn: CKQueryNotification) {
switch ckqn.queryNotificationReason {
case .recordCreated:
// This is where it crashes.. when I try to access ckqn.recordID
CKContainer.default().privateCloudDatabase.fetch(withRecordID: ckqn.recordID!) { (record, error) in
if let record = record {
// Handle creating local notif for record
} else if let error = error {
print("Unable to retrieve record: \(error)")
}
}
default:
break
}
}
After investigating, it seems like I'm getting a CKDatabaseNotification instead of a CKQueryNotification -- How am I suppose to specify that I want a CKQueryNotification?
I finally figured it out. I'm using NSPersistentCloudKitContainer to sync my data across devices. My subscription was observing any changes made to a record created by NSPersistentCloudKitContainer, so it was creating a CKDatabaseNotification every time a change was made, instead of a CKQueryNotification. Since I was trying to process a CKQueryNotification, my app kept crashing since it couldn't find the recordID, hence the error message.
To resolve this, I ended up creating a separate record (not part of NSPersistentCloudKitContainer) in iCloud and made my CKSubscription observe any changes to that record.