Search code examples
iospush-notificationicloudsilentpushsilent-notification

Silent iCloud changed notifications not received in background


My app uses a public iCloud database that is synchronised using push notifications.
The subscription to iCloud notifications uses the following notificationInfo:

    let notificationInfo = CKNotificationInfo()
    notificationInfo.alertBody = nil
    notificationInfo.shouldSendContentAvailable = true  

The test setup uses 2 iOS devices:

  • The 1st device uses my app to modify the iCloud database.
  • The 2nd device runs my app either in foreground or (screen turned off) in background mode. This is done under Xcode control, so that I can set breakpoints. System settings/Notifications of my app: Notifications allowed, shown in notification center & lock screen.

1st test:

2nd device: Executes my app in foreground.

When the 1st device modifies the database, a notification is received in

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) 

This is as expected.

2nd test:

2nd device: As above, but now the screen is turned off, i.e. my app is in background.

When the 1st device modifies the database, a notification is received.

Expected behaviour:
Since shouldSendContentAvailable is set to true in the notification info, the system should wake my app (see docs). The app should then be given background execution time to download any data related to the push notification, such as the set of records that changed. This should be done by calling

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)  

Actual behaviour:
An alert is displayed in the lock screen, saying „iCloud changed, slide to open“. If I swipe this alert and unlock the device, only then is this function actually called.

My problem:
I want to use silence pushes from iCloud to update the local data of my app. So why is an alert with the default message body „iCloud changed“ shown on the lock screen, although I set shouldSendContentAvailable = true ?


Solution

  • My fault, although not obvious:

    My CloudKit Dashboard shows that there is my subscription type. But for some reason, the dashboard does not show any subscription. I thus fetched all existing subscriptions using

    private func getAllSubscriptionIDs(completion: @escaping (_ subscriptionIDs:[String]?, _ error: Error?) -> Void) {
        let database = CKContainer.default().publicCloudDatabase
        database.fetchAllSubscriptions { subscriptions, error in
            guard error == nil else {
                completion(nil, error)
                return
            }
            guard let subscriptions = subscriptions else {
                completion([], nil)
                return
            }
            let subscriptionIDs = subscriptions.map{ $0.subscriptionID }
            completion(subscriptionIDs, nil)
        } // fetchAllSubscriptions
    }  
    

    I got 5 existing subscriptions back, and one of them had an alert body "iCloud changed" with shouldSendContentAvailable = false.

    Obviously, I did use these subscriptions earlier during the development, and I did not delete them since they were not shown in the dashboard.
    Deleting them fixed the problem.