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];