jazzedge
11/30/2017 - 1:48 PM

Swift - CloudKit - Set Subscription Notification for Specific User

See:
https://www.hackingwithswift.com/read/33/8/delivering-notifications-with-cloudkit-push-messages-ckquerysubscription

01. In Appdelegate, add:

import UserNotifications

02. Now put this code into the didFinishLaunchingWithOptions method,
before the return true line:

UNUserNotificationCenter.current().requestAuthorization(options:
[.alert, .sound, .badge]) { granted, error in
    if let error = error {
        print("D'oh:\(error.localizedDescription)")
    } else {
application.registerForRemoteNotifications()
    }
}

registerForRemoteNotifications() creates a unique device token that can be used to message this device. That device token is silently handed off to CloudKit, so we don’t need to do anything other than request it.

03. In your View Controller

@objc func saveTapped() {

    let defaults = UserDefaults.standard

    defaults.set(myGenres, forKey: "myGenres")
    let database = CKContainer.default().publicCloudDatabase
    database.fetchAllSubscriptions { [unowned self] subscriptions, error in
        if error == nil {
            if let subscriptions = subscriptions {
                for subscription in subscriptions {
                    database.delete(withSubscriptionID:subscription.subscriptionID) { str, error in
                        if error != nil {
                        // do your error handling here!
                        print(error!.localizedDescription)
                        }
                    }
                }
                for genre in self.myGenres {
                let predicate = NSPredicate(format:"genre = %@", genre)
                let subscription = CKQuerySubscription(recordType: "Whistles", predicate: predicate, options: .firesOnRecordCreation)
                let notification = CKNotificationInfo()
                notification.alertBody = "There's a new whistle in the \(genre) genre."
                notification.soundName = "default"
                subscription.notificationInfo = notification
                database.save(subscription) { result, error in
                  if let error = error {
                    print(error.localizedDescription)
                  }
                }
              }

            }

        } else {

            // do your error handling here!
            print(error!.localizedDescription)
        }
    }
}

For the sake of completeness, it’s worth adding that if a remote notification arrives while the app is already running, it will be silently ignored. If you want to force iOS to show the alert you must do three things:

1.Make the AppDelegate class conform to UNUserNotificationCenterDelegate.
2.Set your app delegate to be the delegate for the user notification center.
3.Implement user notification center delegate’s willPresent method, calling its completion handler with how you want the alert shown.

To satisfy step 2, add this line of code before return true inside the app delegate’s didFinishLaunchingWithOptions method:

UNUserNotificationCenter.current().delegate = self

For step 3, if you want the alert, sound, and badge of the notification to be shown even when the app is already running, you’d add this method to the app delegate:

func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification, withCompletionHandler
completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert,.sound, .badge])
}

If you want only part of the notification to be used, just change the array being passed to completionHandler().

See: https://stackoverflow.com/questions/47445895/how-can-an-nspredicate-test-that-a-value-contains-one-of-two-other-values

03. To get the user ID and set it in the predicate:

let userID = UUID().uuidString

let predicate = NSPredicate(format: "%K == %@", argumentArray: ["recipient", userID])

NSPredicate *predicate=[NSPredicate predicateWithFormat:@"uid contains[cd] %@ OR uid contains[cd] %@", myID, targetUser];

NSPredicate *p3 = [NSPredicate predicateWithFormat:@"(uid contains[cd] %@) OR (uid contains[cd] %@)", myID, targetUser];