CKFetchRecordZoneChangesOperation 未在共享记录删除时调用 recordWithIDWasDeletedBlock

Posted

技术标签:

【中文标题】CKFetchRecordZoneChangesOperation 未在共享记录删除时调用 recordWithIDWasDeletedBlock【英文标题】:CKFetchRecordZoneChangesOperation not calling recordWithIDWasDeletedBlock on Shared Record Deletion 【发布时间】:2020-07-28 17:16:57 【问题描述】:

我有一条记录从设备 A 共享到设备 B。设备 B 接受共享并在设备 B 的 UI 上显示信息。设备 B 使用 CKDatabaseSubscription 订阅该共享记录上的数据库更改。

然后我删除设备 A 上的该记录,然后触发设备 B 关于数据库更改的通知。然后,我通过使用 CKFetchDatabaseChangesOperation 获取数据库更改来处理设备 B 上的通知。然后,我随后使用 CKFetchRecordZoneChangesOperation 获取区域更改。

我接到了对 recordZoneFetchCompletionBlock 和 fetchRecordZoneChangesCompletionBlock 的调用,但仅此而已。既没有调用recordChangedBlock,也没有调用recordWithIDWasDeletedBlock。

请务必注意,我正在使用完全相同的代码来监视另一种类型的共享记录,并且它可以正常工作。它实际上是一个记录,是这个记录中一个孩子的孩子。所以我不确定为什么这个***/父记录在查询时不会显示为被删除。当设备 A 删除记录时(在其私有 DB 和 DeviceB 的共享 DB 上),该记录确实从 CloudKit 中删除。所以这不是 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];

【问题讨论】:

【参考方案1】:

我遇到这个问题已经有一段时间了,所以细节很模糊。但我为此向一位 Apple 工程师开了一张支持票。

其中的原因(就像 2018 年一样)是,当 CKShare 的父记录被删除并且该记录与您共享时,您只会收到数据库更改通知。您对该记录的访问已结束,因此没有关于它的记录级信息。

我问工程师是否有办法知道记录是否被删除或取消共享或更改的原因,他说没有办法知道。

所以我认为处理这个问题的方法是推断该记录不可访问,然后重新查询它。然后你就会知道它是否可用。

【讨论】:

【参考方案2】:

假设您有一个 ID 为“John”的区域,并且您只有一条记录,如果删除该记录,您将不会收到 recordWithIDWasDeletedBlock,但您会收到 recordZoneWithIDWasDeletedBlock

这意味着这个区域“John”没有更多的记录并且它不再存在所以它被删除了。

但是,如果您在“John”区域中有更多记录并且您从中删除了一条记录,那么您将收到recordWithIDWasDeletedBlock

【讨论】:

以上是关于CKFetchRecordZoneChangesOperation 未在共享记录删除时调用 recordWithIDWasDeletedBlock的主要内容,如果未能解决你的问题,请参考以下文章