I am creating my two CKSubscriptions
in my App Delegate's didFinishLaunchingWithOptions
method as such.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
_myContainer = [CKContainer containerWithIdentifier:@"iCloud.com.isaranjha.Copyfeed"];
_privateDatabase = [_myContainer privateCloudDatabase];
[_privateDatabase fetchSubscriptionWithID:@"subscription" completionHandler:^(CKSubscription *subscription, NSError *error){
if (subscription) {
} else {
NSPredicate *predicate = [NSPredicate predicateWithValue:YES];
CKSubscription *subscription = [[CKSubscription alloc] initWithRecordType:@"Strings" predicate:predicate subscriptionID:@"subscription" options:CKSubscriptionOptionsFiresOnRecordCreation | CKSubscriptionOptionsFiresOnRecordDeletion | CKSubscriptionOptionsFiresOnRecordUpdate];
CKNotificationInfo *notificationInfo = [CKNotificationInfo new];
notificationInfo.alertBody = @"";
notificationInfo.shouldSendContentAvailable = YES;
subscription.notificationInfo = notificationInfo;
[_privateDatabase saveSubscription:subscription completionHandler:^(CKSubscription *subscription, NSError *error) {
}];
}
}];
[_privateDatabase fetchSubscriptionWithID:@"subscription1" completionHandler:^(CKSubscription *subscription, NSError *error){
if (subscription) {
} else {
NSPredicate *predicate1 = [NSPredicate predicateWithValue:YES];
CKSubscription *subscription1 = [[CKSubscription alloc] initWithRecordType:@"Images" predicate:predicate1 subscriptionID:@"subscription1" options:CKSubscriptionOptionsFiresOnRecordCreation | CKSubscriptionOptionsFiresOnRecordDeletion | CKSubscriptionOptionsFiresOnRecordUpdate];
CKNotificationInfo *notificationInfo1 = [CKNotificationInfo new];
notificationInfo1.shouldSendContentAvailable = YES;
notificationInfo1.alertBody = @"";
subscription1.notificationInfo = notificationInfo1;
[_privateDatabase saveSubscription:subscription1 completionHandler:^(CKSubscription *subscription, NSError *error) {
}];
}
}];
ViewController *view = [[ViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:view];
self.window.rootViewController = navController;
return YES;
}
Those are created successfully, as when I log out NSError
, it returns null and every time I open the app after that, it is able to fetch them correctly. However, when a record is created or deleted, on one device, say an iPhone, the notification doesn't fire (or it is not being properly received) on the other device, say a Mac. So here is how I am listening for the notifications on my Mac.
- (void)application:(NSApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(@"CKSubscription received.");
CKQueryNotification *cloudKitNotification = [CKQueryNotification notificationFromRemoteNotificationDictionary:userInfo];
[[NSNotificationCenter defaultCenter] postNotificationName:@"CloudKitUpdated" object:nil userInfo:@{@"ckNotification" : cloudKitNotification}];
}
That NSLog
unfortunately never fires.
You have an empty alertbody
notificationInfo1.alertBody = @"";
With that you won't receive push notifications when your app is not active. When you manually activate your app you will be able to query for the notifications using CKFetchNotificationChangesOperation. Here is a snippet of how I use it in EVCloudKitDao:
public func fetchChangeNotifications(skipRecordID: CKRecordID?, inserted:(recordID:String, item: EVCloudKitDataObject) -> Void, updated:(recordID: String, item: EVCloudKitDataObject) -> Void, deleted:(recordId: String) -> Void, completed:()-> Void) {
var defaults = NSUserDefaults.standardUserDefaults()
var array: [NSObject] = [NSObject]()
var operation = CKFetchNotificationChangesOperation(previousServerChangeToken: self.previousChangeToken)
operation.notificationChangedBlock = { notification in
if notification.notificationType == .Query {
if var queryNotification = notification as? CKQueryNotification {
array.append(notification.notificationID)
if skipRecordID != nil && skipRecordID?.recordName != queryNotification.recordID.recordName {
if queryNotification.queryNotificationReason == .RecordDeleted {
deleted(recordId: queryNotification.recordID.recordName)
} else {
EVCloudKitDao.publicDB.getItem(queryNotification.recordID.recordName, completionHandler: { item in
EVLog("getItem: recordType = \(EVReflection.swiftStringFromClass(item)), with the keys and values:")
EVReflection.logObject(item)
if queryNotification.queryNotificationReason == .RecordCreated {
inserted(recordID: queryNotification.recordID.recordName, item: item)
} else if queryNotification.queryNotificationReason == .RecordUpdated {
updated(recordID: queryNotification.recordID.recordName, item: item)
}
}, errorHandler: { error in
EVLog("ERROR: getItem for change notification.\n\(error.description)")
})
}
}
}
}
}
operation.fetchNotificationChangesCompletionBlock = { changetoken, error in
var op = CKMarkNotificationsReadOperation(notificationIDsToMarkRead: array)
op.start()
EVLog("changetoken = \(changetoken)")
self.previousChangeToken = changetoken
if operation.moreComing {
self.fetchChangeNotifications(skipRecordID, inserted: inserted, updated: updated, deleted: deleted, completed:completed)
} else {
completed()
}
}
operation.start()
}
when your app is active, then you should receive the notifications in the application didReceiveRemoteNotification.