Subscribing to Record Changes

It’s inefficient for your app to repeat a query when the results are mostly the same as the last query. Instead, subscribe to record changes, and let the server run the query in the background. The server will notify your app of changes that interest the user or app. For example, if one user of your app is interested in artwork by a certain artist, your app can be notified when new artwork by that artist is uploaded.

../Art/subscriptions_2x.png

Save Subscriptions to the Database

In your code, create a subscription object specifying the record type, predicate, and types of changes you want to be notified about. Then save the subscription object to the database.

To create and save a subscription

  1. Create a predicate object.

    For example, subscribe to artwork from an artist (where the artist field in the Artwork record type is a Reference type).

    CKRecordID *artistRecordID = [[CKRecordID alloc] initWithRecordName:@"Mei Chen"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"artist = %@", artistRecordID];
  2. Create a subscription specifying the record type, predicate, and notification options.

    CKSubscription *subscription = [[CKSubscription alloc]
                                        initWithRecordType:@"Artwork"
                                        predicate:predicate
                                        options:CKSubscriptionOptionsFiresOnRecordCreation];

    The possible values for the options parameter are: CKSubscriptionOptionsFiresOnRecordCreation, CKSubscriptionOptionsFiresOnRecordDeletion, CKSubscriptionOptionsFiresOnRecordUpdate, and CKSubscriptionOptionsFiresOnce. Because the options parameter is a bitmask, you can subscribe to any combination of the type of changes. For example, you can pass CKSubscriptionOptionsFiresOnRecordCreation | CKSubscriptionOptionsFiresOnRecordUpdate as the options: parameter to receive notification of all new data.

  3. Create a CloudKit notification object.

    CKNotificationInfo *notificationInfo = [CKNotificationInfo new];
    notificationInfo.alertLocalizationKey = @"New artwork by your favorite artist.";
    notificationInfo.shouldBadge = YES;

    To display a localized string to the user, set the notification’s alertLocalizationKey property (not the alertBody property).

  4. Set the subscription’s notification object to the new CloudKit notification object.

    subscription.notificationInfo = notificationInfo;
  5. Save the subscription to the database.

    CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];
        [publicDatabase saveSubscription:subscription
                        completionHandler:^(CKSubscription *subscription, NSError *error) {
                            if (error)
                                // insert error handling
                        }
         ];

In Xcode, run your app to save the subscription to the database.

Register for Push Notifications

Saving subscriptions to the database doesn’t automatically configure your app to receive notifications when a subscription fires. CloudKit uses the Apple Push Notification service (APNs) to send subscription notifications to your app, so your app needs to register for push notifications to receive them.

For iOS and tvOS apps, add this code to the application:didFinishLaunchingWithOptions: protocol method to register for push notifications:

    // Register for push notifications
    UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil];
    [application registerUserNotificationSettings:notificationSettings];
    [application registerForRemoteNotifications];

For Mac apps, implement the applicationDidFinishLaunching: protocol method to register for push notifications.

Optionally implement the application:didRegisterForRemoteNotificationsWithDeviceToken: and application:didFailToRegisterForRemoteNotificationsWithError: methods to take the appropriate action when the app successfully or unsuccessfully registers for push notifications.

Handle Push Notifications in Code

Next, implement the application:didReceiveRemoteNotification: method to process subscription notifications when they arrive. For iOS and tvOS apps, implement the UIApplicationDelegate protocol method and for Mac apps, implement the NSApplicationDelegate protocol method. For example, implement this method to update views when records matching your predicate are created, updated, or deleted.

  1. Add the application:didReceiveRemoteNotification: protocol method to the app’s delegate.

    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    }
  2. In the application:didReceiveRemoteNotification: method, convert the userInfo parameter to aCKNotification object.

    CKNotification *cloudKitNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
  3. Get the body of the notification.

    NSString *alertBody = cloudKitNotification.alertBody;
  4. Get the new or modified record from the CKQueryNotification object.

    if (cloudKitNotification.notificationType == CKNotificationTypeQuery) {
       CKRecordID *recordID = [(CKQueryNotification *)cloudKitNotification recordID];
    }
  5. Update views or notify the user according to the record changes.

Test Subscriptions

You can initially test subscriptions by running your app through Xcode and using CloudKit Dashboard to create, modify, or delete records, as described in Add, Modify, and Delete Records. Then fully test subscriptions by running your app on multiple devices. Use one device to make changes and another device to receive the subscription notifications. You use multiple devices because a notification isn’t sent to the same device that originated the notification.

For iOS and tvOS, use a device connected to your Mac (not a simulator) to test subscription notifications. Your app successfully registers for push notifications if a dialog that asks the user’s permission for your app to receive notifications.

Recap

In this chapter you learned how to: