1

I'm trying to let the iOS app listen to CKQuerySubscription changes. Data is transmitted by a remote iOS app. I already have a macOS application, which does receive data sent by the remote iOS app. The iOS app I have trouble with already has a subscription. Yet, its AppDelegate never receives a call in the didReceiveRemoteNotification method.

import UIKit
import UserNotifications
import CloudKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        /* notifications */
        let center  = UNUserNotificationCenter.current()
        center.delegate = self
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            switch settings.authorizationStatus {
            case .authorized:
                print("You already have permission")
                DispatchQueue.main.async() {
                    application.registerForRemoteNotifications()
                }
            case .denied:
                print("setting has been disabled")
            case .notDetermined:
                print("Let me ask")
                UNUserNotificationCenter.current().requestAuthorization(options: []) { (granted, error) in
                    if error == nil {
                        if granted {
                            print("you are granted permission")
                            DispatchQueue.main.async() {
                                application.registerForRemoteNotifications()
                            }
                        }
                    }
                }
            }
        }
        return true
    }
}

    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register notifications_ error:", error)
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        print("Receiving data...") // never called...
    }
}

I have some capabilities on as shown below. I don't know if the app needs push notifications. For now, it's turned on.

So why doesn't my iOS app get the remote notification call? I'm using the app with an actual device, not a simulator. Thanks.

enter image description here

EDIT: Creating a subscription to a record change

class HomeViewController: UIViewController {
    override func viewDidLoad() {
        registerSubscription()
    }

    func registerSubscription() {
        let cloudContainer = CKContainer(identifier: "iCloud.com.xxx.XXXXX")
        let privateDB = cloudContainer.privateCloudDatabase
        let predicate = NSPredicate(format: "TRUEPREDICATE")
        let subscription = CKQuerySubscription(recordType: "PrivateRecords", predicate: predicate, options: .firesOnRecordCreation)
        let notification = CKNotificationInfo()
        subscription.notificationInfo = notification
        privateDB.save(subscription, completionHandler: ({returnRecord, error in
            if let err = error {
                print("Subscription has failed: \(err.localizedDescription)")
            } else {
                print("Subscription set up successfully")
                print("Subscription ID: \(subscription.subscriptionID)")
            }
        }))
    }
}
El Tomato
  • 6,479
  • 6
  • 46
  • 75

1 Answers1

1

There are a few more things you can check.

First, make sure you implement didReceiveRemoteNotification in your app delegate:

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 Received: \(sub)")
  }
}

There are also a few other things you can check:

  1. Try deleting your CKQuerySubscription in the CloudKit dashboard, then run your iOS code again that registers it. Does the subscription show up in the dashboard?
  2. Does the CloudKit log show that a notification was sent? It lists all notifications that were pushed to a device.
  3. If you are using silent push notifications, try enabling Background fetch in the Background Modes capability (right above Remote notifications).

If you do all that and it still doesn't work, can you share your CKQuerySubscription code?

-- Update --

Try setting some additional attributes on your CKNotificationInfo object. There are some obscure bugs with notifications that can usually be circumvented by setting a couple properties like this:

notification.shouldSendContentAvailable = true
notification.alertBody = "" //(Yes, a blank value. It affects the priority of the notification delivery)

You can also try setting your predicate to: NSPredicate(value: true)

Also, what does your privateDB.save method return? Does it say it succeeds or fails?

Clifton Labrum
  • 13,053
  • 9
  • 65
  • 128
  • Thanks for the tips. I already have the didReceiveRemoteNotification method. For No. 1, the subscription never appears in the dashboard for the iOS app. It has the one for the macOS app. For No. 2, the macOS counterpart receives trasmitted data. So that's not a question. For No. 3, I have turned Background fetch on and off to see if it makes any difference. – El Tomato Sep 05 '18 at 21:37
  • Then it sounds like your `CKQuerySubscription` is the problem if it never shows up in the CloudKit dashboard. Can you update your question to include your code where you register the subscription? – Clifton Labrum Sep 05 '18 at 21:50
  • I just updated my answer with a couple more things to try and another question. – Clifton Labrum Sep 05 '18 at 23:57
  • Thanks. But the dashboard doesn't add a new subscription for the iOS app. If I delete the one for macOS on the dashboard and run that application, the dashboard will add a new one. – El Tomato Sep 06 '18 at 00:05
  • What does your `privateDB.save` method return? Does it say it succeeds or fails? – Clifton Labrum Sep 06 '18 at 00:27
  • It's successful. – El Tomato Sep 06 '18 at 00:30
  • I think the dashboard won't add a new subscription for the same notification option. If I change the option to .firesOnRecordUpdate, the dashboard shows a new one. – El Tomato Sep 06 '18 at 00:49
  • CKQuerySubscription for iOS is terribly difficult to work with. I now have it working. I don't know what was missing before. I find it odd for me to say it because it's often difficult to get information on code for macOS. – El Tomato Sep 10 '18 at 06:18