How to receive cloudkit notifications about change

2019-03-15 09:33发布

问题:

I have two icloud accounts (A and B) on two different devices. From one of them (A) I share ckrecord to another one (B) like this:

let controller = UICloudSharingController { controller, preparationCompletionHandler in

    let share = CKShare(rootRecord: record)
    share[CKShareTitleKey] = "title" as CKRecordValue

    share[CKShareTypeKey] = "pl.blueworld.fieldservice" as CKRecordValue
    share.publicPermission = .readWrite

    let modifyOperation = CKModifyRecordsOperation(recordsToSave: [record, share], recordIDsToDelete: nil)
    modifyOperation.savePolicy = .ifServerRecordUnchanged
    modifyOperation.perRecordCompletionBlock = { record, error in
        print(error?.localizedDescription ?? "")
    }

    modifyOperation.modifyRecordsCompletionBlock = { records, recordIds, error in

        print(share.url)
        preparationCompletionHandler(share, CloudAssistant.shared.container, error)
    }

    CloudAssistant.shared.container.privateCloudDatabase.add(modifyOperation)
}

controller.delegate = self

UIViewController.top()?.present(controller, animated: true)

When second device (B) did accept cloudkit share I fetch record and subscribe for changes:

func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {

    let acceptSharesOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
    acceptSharesOperation.perShareCompletionBlock = { metadata, share, error in

        if let error = error {
            UIAlertController.show(withMessage: error.localizedDescription)
        } else {

            let operation = CKFetchRecordsOperation(recordIDs: [cloudKitShareMetadata.rootRecordID])
            operation.perRecordCompletionBlock = { record, _, error in

                if let error = error {
                    UIAlertController.show(withMessage: error.localizedDescription)
                } else if let record = record {
                    CloudAssistant.shared.save(records: [record], recordIDsToDelete: [])

                    let options: CKQuerySubscriptionOptions = [.firesOnRecordCreation, .firesOnRecordUpdate, .firesOnRecordDeletion]
                    let territorySubscription = CKQuerySubscription(recordType: "Territory", predicate: NSPredicate(value: true), options: options)

                    let notificationInfo = CKNotificationInfo()
                    notificationInfo.shouldBadge = false
                    notificationInfo.shouldSendContentAvailable = true

                    territorySubscription.notificationInfo = notificationInfo

                    CloudAssistant.shared.sharedDatabase?.save(territorySubscription) { _, _ in }
                }
            }

            CloudAssistant.shared.container.sharedCloudDatabase.add(operation)
        }
    }
    acceptSharesOperation.qualityOfService = .userInteractive
    CKContainer(identifier: cloudKitShareMetadata.containerIdentifier).add(acceptSharesOperation)
}

Now from device A I sucessfully (I am sure about that, changes is saved in iCloud) perform an update on a record shared with others. But device B doesnt know about that, unless I fetch record manually once again.

But from the other side, it works pretty well.

If I successfully perform an update on a record shared with me (on device B) then device A mnagically gets a notification about change and everything is fine. What makes the difference?

How to subscribe for changes on a records shared with me?

P.S when I will be able I will start a bount of 200 or even more for someone who will help me with this.

iOS 11, Swift 4, Xcode 9

回答1:

Here's my checklist for debugging subscription notifications not appearing as expected. Sounds like you may have ruled some of these out already.

  1. Make sure the app is registered for notifications
  2. Make sure notifications are enabled on the device for this app
  3. Make sure all devices are using the same container
  4. On the next app startup, read all subscriptions using fetchAllSubscriptionsWithCompletionHandler and NSLog each sub's details (especially: subscriptionID, trigger options, record type and the predicate). Verify that the expected subs exist. Verify that each sub's predicate matches expectations (in this case, compare the predicates you find on both devices).

I wasted a bunch of time debugging "missing" notifications when:

  1. My locally-built version was using the TEST environment and the TestFlight users were accessing the PROD environment. Debugging step 2 found this.
  2. When inadvertently re-using a subscription ID, thus each new sub overwrote the prior one. Debugging step 4 eventually revealed the problem

So far, these four debugging steps have helped me understand all of my "missing notification" problems. Once I understood why the notifs didn't appear, that narrowed down which block of code was responsible.