jazzedge
11/29/2017 - 8:20 PM

Swift - CloudKit Subscriptions - 4

Subscriptions are one of the most valuable CloudKit features. They build on Apple’s notification infrastructure to allow various clients to get push notifications when certain CloudKit changes occur.

See: https://www.whatmatrix.com/blog/a-guide-to-cloudkit-how-to-sync-user-data-across-ios-devices/

Subscriptions are one of the most valuable CloudKit features. They build on Apple’s notification infrastructure to allow various clients to get push notifications when certain CloudKit changes occur. These can be normal push notifications familiar to iOS users (such as sound, banner, or badge), or in CloudKit, they can be a special class of notification called silent pushes. These silent pushes happen entirely without user visibility or interaction, and as a result, don’t require the user to enable push notification for your app, saving you many potential user-experience headaches as an app developer.

The way to enable these silent notifications is to set the shouldSendContentAvailable property on the CKNotificationInfo instance, while leaving all of the traditional notification settings (shouldBadge, soundName, and so on) unset.

Note also, I am using a CKQuerySubscription with a very simple “always true” predicate to watch for changes on the one (and only) Note record. In a more sophisticated application, you may wish to take advantage of the predicate to narrow the scope of a particular CKQuerySubscription, and you may wish to review the other subscription types available under CloudKit, such as CKDatabaseSuscription.

Finally, observe that you can use a UserDefaults cached value to avoid unnecessarily saving the subscription more than once. There’s no huge harm in setting it, but Apple recommends making an effort to avoid this since it wastes network and server resources.

// Create the CloudKit subscription we’ll use to receive notification of changes.
// The SubscriptionID lets us identify when an incoming notification is associated
// with the query we created.
public let subscriptionID = "cloudkit-note-changes"
private let subscriptionSavedKey = "ckSubscriptionSaved"
public func saveSubscription() {
	// Use a local flag to avoid saving the subscription more than once.
	let alreadySaved = UserDefaults.standard.bool(forKey: subscriptionSavedKey)
	guard !alreadySaved else {
		return
	}
		
	// If you wanted to have a subscription fire only for particular
	// records you can specify a more interesting NSPredicate here.
	// For our purposes we’ll be notified of all changes.
	let predicate = NSPredicate(value: true)
	let subscription = CKQuerySubscription(recordType: "note",
	                                       predicate: predicate,
	                                       subscriptionID: subscriptionID,
	                                       options: [.firesOnRecordCreation, .firesOnRecordDeletion, .firesOnRecordUpdate])
		
	// We set shouldSendContentAvailable to true to indicate we want CloudKit
	// to use silent pushes, which won’t bother the user (and which don’t require
	// user permission.)
	let notificationInfo = CKNotificationInfo()
	notificationInfo.shouldSendContentAvailable = true
	subscription.notificationInfo = notificationInfo
		
	let operation = CKModifySubscriptionsOperation(subscriptionsToSave: [subscription], subscriptionIDsToDelete: [])
	operation.modifySubscriptionsCompletionBlock = { (_, _, error) in
		guard error == nil else {
			return
		}

		UserDefaults.standard.set(true, forKey: self.subscriptionSavedKey)
	}
	operation.qualityOfService = .utility
		
	let container = CKContainer.default()
	let db = container.privateCloudDatabase
	db.add(operation)
}