iosmacoscloudkitcksubscriptionckrecord

CKSubscription not working


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.


Solution

  • 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.