CKFetchNotificationChangesOperation returning old

2019-02-14 14:25发布

问题:

I'm working on a CloudKit-based app that uses CKSubscription notifications to keep track of changes to a public database. Whenever the app receives a push notification I check the notification queue with CKFetchNotificationChangesOperation and mark each notification read after processing it:

    __block NSMutableArray *notificationIds = [NSMutableArray new];

CKFetchNotificationChangesOperation *operation = [[CKFetchNotificationChangesOperation alloc] initWithPreviousServerChangeToken:self.serverChangeToken];
operation.notificationChangedBlock = ^(CKNotification *notification) {
    [notificationIds addObject:notification.notificationID];
    [self processRemoteNotification:notification withCompletionHandler:completionHandler];
};

__weak CKFetchNotificationChangesOperation *operationLocal = operation;
operation.fetchNotificationChangesCompletionBlock = ^(CKServerChangeToken *serverChangeToken, NSError *operationError) {
    if (operationError) {
        NSLog(@"Unable to fetch queued notifications: %@", operationError);
    }
    else {
        self.serverChangeToken = serverChangeToken;
        completionHandler(UIBackgroundFetchResultNewData);

        // Mark the processed notifications as read so they're not delivered again if the token gets reset.
        CKMarkNotificationsReadOperation *markReadOperation = [[CKMarkNotificationsReadOperation alloc] initWithNotificationIDsToMarkRead:[notificationIds copy]];
        [notificationIds removeAllObjects];

        markReadOperation.markNotificationsReadCompletionBlock = ^(NSArray *notificationIDsMarkedRead, NSError *operationError) {
            if (operationError) {
                NSLog(@"Unable to mark notifications read: %@", operationError);
            }
            else {
                NSLog(@"%lu notifications marked read.", (unsigned long)[notificationIDsMarkedRead count]);
            }
        };

        [[CKContainer defaultContainer] addOperation:markReadOperation];

        if (operationLocal.moreComing) {
            NSLog(@"Fetching more");
            [self checkNotificationQueueWithCompletionHandler:completionHandler];
        }
    }
};

[[CKContainer defaultContainer] addOperation:operation];

As I understand it marking a notification read will keep it from showing up in future queue fetches, even if the server change token is reset to nil. Instead I'm getting a lot of old notifications in every fetch with a non-nil change token when there should only be 1 or 2 new ones. I can detect the old ones from the notificationType flag, but I'm concerned that they're showing up at all. Am I missing a step somewhere?

回答1:

I know this is a bit old, but I was running into the same issue. I think I figured it out (at least for my case).

In my code, I was doing the same as you: that is, adding all the notificationIDs to an array and using that in my CKMarkNotificationsReadOperation, and was also getting all the notifications returned each time (although, as you noted, with a type of "ReadNotification").

I changed my code so that I was only adding "new" notifications to my array, and not the "ReadNotification" items, and sending those. That fixed it.

It seems that sending a notification back to the server to be marked as read, even if it already has been marked as such, will cause it to be returned again as "ReadNotification."

I hope this helps someone.



回答2:

The documentation isn't very clear it should say: "Marking a notification as read prevents it from being returned by subsequent fetch operations"...as a query notification type. Further clarification it should say the notifications will instead be returned as read type.

If it wasn't returned at all then other devices that missed the push wouldn't know that something has changed!