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
удален, и эта запись была предоставлена вам, вы получите только уведомление об изменении базы данных. Ваш доступ к записи закончился, поэтому информация на уровне записи об этом отсутствует.
Я спросил инженера, есть ли способ узнать, была ли запись удалена, не предоставлена к совместному использованию или почему она изменилась, и он сказал, что нет способа узнать.
Поэтому я думаю, что способ справиться с этим - просто сделать вывод, что запись недоступна, а затем повторно запросить ее. Тогда вы узнаете, доступно оно или нет.