CKFetchRecordZoneChangesOperation не вызывает recordWithIDWasDeletedBlock при удалении общей записи

У меня есть запись, которой я делюсь с устройства A на устройство B. Устройство B принимает общий доступ и отображает информацию в пользовательском интерфейсе устройства B. Устройство B подписывается на изменения базы данных в этой общей записи с помощью CKDatabaseSubscription.

Затем я удаляю эту запись на устройстве A, которое затем вызывает уведомление на устройство B об изменении базы данных. Затем я обрабатываю это уведомление на устройстве B, получая изменения БД с помощью CKFetchDatabaseChangesOperation. Затем я получаю изменения зоны, используя CKFetchRecordZoneChangesOperation.

Я получаю вызовы recordZoneFetchCompletionBlock и fetchRecordZoneChangesCompletionBlock, но это все. Ни recordChangedBlock, ни recordWithIDWasDeletedBlock не вызываются.

Важно отметить, что я использую тот же самый код для отслеживания общих записей другого типа, и он работает. На самом деле это запись, являющаяся потомком ребенка в этой записи. Поэтому я не уверен, почему эта родительская запись верхнего уровня не отображается как удаляемая при запросе. Запись делает удаление из CloudKit, когда Устройство A удаляет его (как на своей частной БД и на общей БД) прибора В. Так что я не верю, что это не отношения или какие-либо ограничения в отношении CloudKit.

Приветствую любые мысли о том, как его догнать. Был на этом неделю.

Это код, который я использую для подписки...

+ (void) subscribeToGameChangesInDB:(int)dbID success:(void (^) (void))success failure:(void (^)(NSError *error))failure
{
    CKNotificationInfo *notif = [[CKNotificationInfo alloc] init];

    notif.alertLocalizationKey = @"DB Changed";
    notif.shouldBadge = NO;
    notif.shouldSendContentAvailable = YES;

    CKDatabase *db = [CloudKitManager getDBForIdentifier:dbID];

    if (dbID == kCKDBPublic)
    {
        NSLog(@"ERROR : Cannot subscribe to database changes in the PUBLIC DB.");
    }
    else
    {
        CKDatabaseSubscription *subscription = [[CKDatabaseSubscription alloc] initWithSubscriptionID:[NSString stringWithFormat:@"AllDBhanges-%@", [CloudKitManager getDatabaseNameForScope:db.databaseScope]]];

        subscription.notificationInfo = notif;

        [CloudKitManager saveSubscription:subscription inDB:db success:success failure:failure];
    }
}

+ (void) saveSubscription:(CKSubscription*)subscription inDB:(CKDatabase*)db success:(void (^) (void))success failure:(void (^)(NSError *error))failure
{
    [db saveSubscription:subscription completionHandler:^(CKSubscription * _Nullable subscription, NSError * _Nullable error)
     {
         if (error)
         {
             if ((error.code == CKErrorServerRejectedRequest) || (error.code == CKErrorUnknownItem)) // We probably already subscribed from another device.
             {
                 NSLog(@"Already Subscribed... Passing Success. (code:%ld)", (long)error.code);
                 success ();
             }
             else
             {
                 failure (error);
             }
         }
         else
         {
             success();
         }
     }];
}

Вот код, который я использую для обработки уведомления и выполнения двух описанных выше операций.

- (void) handleDatabaseNotification:(CKDatabaseNotification*)dbNotif
{
    CKDatabase *db = [self getDatabaseForNotificationDatabaseScope:dbNotif.databaseScope];
            
    [self handleDatabase:db changesWithPreviousChangeToken:[self getServerDBChangeTokenFromDisk] withCompletion:^(NSError * _Nonnull error) {

        if (error)
        {
            NSLog (@"ERROR : HAndling Database change notificiation : %@", error);
        }
    }];
}

- (void) handleDatabase:(CKDatabase*)db changesWithPreviousChangeToken:(CKServerChangeToken*)previousChangeToken withCompletion:(void (^)(NSError *error))completion
{
    CKFetchDatabaseChangesOperation *operation = [[CKFetchDatabaseChangesOperation alloc] initWithPreviousServerChangeToken:previousChangeToken];

    [operation setRecordZoneWithIDChangedBlock:^(CKRecordZoneID * _Nonnull zoneID) {

        NSLog(@"ZONE CHANGE : %@", zoneID);

        [self handleDatabase:db zoneID:zoneID changesWithPreviousChangeToken:[self getServerZonesChangeTokenFromDisk]];
    }];

    [operation setFetchDatabaseChangesCompletionBlock:^(CKServerChangeToken * _Nullable serverChangeToken, BOOL moreComing, NSError * _Nullable operationError) {

        if (operationError)
        {
            NSLog (@"ERROR : Unable to fetch Zone changes : %@", operationError);
            
            completion(operationError);
        }

        NSLog(@"New serverChangeToken : %@", serverChangeToken);
                    
        [self storeServerDBChangeTokenToDisk:serverChangeToken];
        
        if (moreComing)
        {
            [self handleDatabase:db changesWithPreviousChangeToken:serverChangeToken withCompletion:completion];
        }
        else
            completion (nil);
    }];

    [db addOperation:operation];
}

- (void) handleDatabase:(CKDatabase*)db zoneID:(CKRecordZoneID*)zoneID changesWithPreviousChangeToken:(CKServerChangeToken*)previousChangeToken
{
    NSLog(@"Zone Changes. DB : %@\nzoneID : %@", db, zoneID);

    CKFetchRecordZoneChangesConfiguration *config = [[CKFetchRecordZoneChangesConfiguration alloc] init];

    config.previousServerChangeToken = previousChangeToken;

    CKFetchRecordZoneChangesOperation *zoneOperation = [[CKFetchRecordZoneChangesOperation alloc] initWithRecordZoneIDs:@[zoneID] configurationsByRecordZoneID:@{zoneID: config}];
    
    [zoneOperation setRecordZoneFetchCompletionBlock:^(CKRecordZoneID * _Nonnull recordZoneID, CKServerChangeToken * _Nullable serverChangeToken, NSData * _Nullable clientChangeTokenData, BOOL moreComing, NSError * _Nullable recordZoneError) {
    
        if (recordZoneError)
        {
            NSLog(@"ERROR : recordZoneError : %@", recordZoneError);
        }
        else
        {
            [self storeServerZonesChangeTokenToDisk:serverChangeToken];

            if (moreComing)
            {
                NSLog(@"*********** There's MORE COMING ***************");
                
                [self handleDatabase:db zoneID:zoneID changesWithPreviousChangeToken:serverChangeToken];
            }
            
            /* Handle changes */
        }
    }];
    
    [zoneOperation setRecordChangedBlock:^(CKRecord * _Nonnull record) {
        
        [self handleDatabaseRecordChange:record];
        
    }];
    
    [zoneOperation setFetchRecordZoneChangesCompletionBlock:^(NSError * _Nullable operationError) {

        NSLog (@"setFetchRecordZoneChangesCompletionBlock");
        
        if (operationError)
            NSLog (@"setFetchRecordZoneChangesCompletionBlock ERROR : %@", operationError);

    }];
    
    [zoneOperation setRecordZoneChangeTokensUpdatedBlock:^(CKRecordZoneID * _Nonnull recordZoneID, CKServerChangeToken * _Nullable serverChangeToken, NSData * _Nullable clientChangeTokenData) {
        
        NSLog(@"setRecordZoneChangeTokensUpdatedBlock Called : \n\nserverChangeToken = %@\n\nclientChangeTokenData = %@\n", serverChangeToken, clientChangeTokenData);

    }];

    [zoneOperation setRecordWithIDWasDeletedBlock:^(CKRecordID * _Nonnull recordID, CKRecordType  _Nonnull recordType) {

        NSManagedObjectContext *context = [self.coreManager getContext];

        [context performBlockAndWait:^{

            [self handleDeleteRecordID:recordID recordType:recordType];

            [self.coreManager deleteContext:context];
        }];
    }];

    [db addOperation:zoneOperation];
}

2 ответа

Допустим, у вас есть зона с идентификатором "John", и у вас есть только одна запись в ней, и если эта запись будет удалена, вы не получите recordWithIDWasDeletedBlock но вы получите recordZoneWithIDWasDeletedBlock.

Это означает, что в этой зоне "Джон" больше нет записей и она больше не существует, поэтому она удаляется.

Но если у вас будет больше записей в зоне "Джон" и вы удалите из нее одну запись, то вы получите recordWithIDWasDeletedBlock.

Я давно не сталкивался с этой проблемой, поэтому детали нечеткие. Но я обратился в службу поддержки с инженером Apple по этому поводу.

В итоге (это было похоже на 2018 год) было то, что когда родительская запись CKShareудален, и эта запись была предоставлена ​​вам, вы получите только уведомление об изменении базы данных. Ваш доступ к записи закончился, поэтому информация на уровне записи об этом отсутствует.

Я спросил инженера, есть ли способ узнать, была ли запись удалена, не предоставлена ​​к совместному использованию или почему она изменилась, и он сказал, что нет способа узнать.

Поэтому я думаю, что способ справиться с этим - просто сделать вывод, что запись недоступна, а затем повторно запросить ее. Тогда вы узнаете, доступно оно или нет.

Другие вопросы по тегам