11/30/2017 - 11:02 AM

Swift - CloudKit - Push Notifications

See: https://code.tutsplus.com/tutorials/an-introduction-to-the-usernotifications-framework--cms-27250
For local notifications, see: https://medium.com/yay-its-erica/usernotifications-in-swift-3-part-1-d250e54440c1
See: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html
See: https://www.appcoda.com/ios10-user-notifications-guide/
Subscribe to record changes: https://developer.apple.com/library/content/documentation/DataManagement/Conceptual/CloudKitQuickStart/SubscribingtoRecordChanges/SubscribingtoRecordChanges.html

01. Open CloudKit dashboard

02. To send push notifications to our app's users, we are going to create a GlobalNotification record type which will notify all users when a new record of this type is created

03. If the Record Type section of the CloudKit dashboard isn't open already for you after logging in, click on it in the sidebar.

04. To create a new record type, click on the + button at the top of the screen, and add:

Title: GlobalNotificationSecurity: 
3Metadata indexes: 
7Field name: content
Field type: string
Index: tick Sort, Query and Search boxes

Then click on the Save button in the bottom-right corner.
05: Back in Xcode, open up your AppDelegate.swift file and add the following import statements at the top of your code:
import CloudKitimport UserNotifications
We are going to use the UserNotifications to manage both notification permissions and incoming notifications.
See: http://code.tutsplus.com/tutorials/an-introduction-to-the-usernotifications-framework--cms-27250

06: Next, edit your AppDelegate class definition to make it conform to the UNUserNotificationCenterDelegate protocol:

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {    ...}

07. Now we need to request permission to show notifications. To do so, replace your application(_:didFinishLaunchingWithOptions:) method with the following: 

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {         UNUserNotificationCenter.current().delegate = self    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { authorized, error in        if authorized {            application.registerForRemoteNotifications()        }    })         return true}

With this code, we ask the user for permission to show notifications and if they accept, register the app for push notifications.

08. Now, we need to implement the application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method. Add the following code to your AppDelegate class: 

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {    let subscription = CKQuerySubscription(recordType: "GlobalNotification", predicate: NSPredicate(format: "TRUEPREDICATE"), options: .firesOnRecordCreation)         let info = CKNotificationInfo()    info.alertBody = "A new notification has been posted!"    info.shouldBadge = true    info.soundName = "default"         subscription.notificationInfo = info         CKContainer.default().publicCloudDatabase.save(subscription, completionHandler: { subscription, error in        if error == nil {            // Subscription saved successfully        } else {            // An error occurred        }    })}

With this code, we first create a CKQuerySubscription object. This subscription object is what tells CloudKit what it should send notifications for. When initialising this subscription, we include the following:

A. The name of the record type to monitor.
B. A predicate which can be used to limit the subscription to particular records of your specified type. In this tutorial, we want to subscribe to all GlobalNotification records so we pass in a predicate which is always true.
C. One or more triggers telling CloudKit when a notification should be sent. In this example, we only want a notification when a new record is created. Other valid triggers include when a record is updated or deleted.

09. Next we create a CKNotificationInfo object, provide it with some content, and then assign it to the subscription object. The CKNotificationInfo object is how you format the push notification that is delivered to your users. In this example, we are configuring it with the following properties:

A. An alertBody of "A new notification has been posted!" 
B. A shouldBadge value of true. This will cause the app icon on the home screen to increment its number for every notification that comes in.
C. A soundName of "default". This will just use the default iOS notification sound for your app. If you want to use a custom sound, then you will need to use the name of your sound file.
D. A desiredKeys array with a single value. For every key you include in this array, CloudKit will load the corresponding value from the record that triggered the notification and include it in the notification's user information dictionary.

Further down, we assign the notification info to the subscription, which is then saved to the public CloudKit database for your app. 

10. So that you can see the notification while your app is running, add the following method to your AppDelegate class:

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

This code uses the UserNotifications framework to show your notification as if your app wasn't running at all.
Before running your app, you will need to check if your test device is logged into an iCloud account by going to Settings. If not, you will need to do so because the CloudKit APIs require the current user to be logged in.
Build and run your app from Xcode and accept your app's request to show notifications. Once you have done that, go back to the CloudKit dashboard for your app and click on Subscription Types in the left sidebar. You should see a new subscription for GlobalNotification records with an INSERT trigger:

11. Sending a Notification

Now, all that's left for us to do is to send a notification! Still in the CloudKit dashboard, click on Default Zone under Public Data in the left sidebar. Click either on the + button or on New Record to create a new GlobalNotification record. Fill in the content with whatever you want and click Save in the bottom right corner.
Once your record has been saved, you should see the notification appear straight away on your test device.