Ошибка сохранения записи CKRecordID на сервере: неверная попытка обновить запись с типа "X" на "Y"

Пока я пользуюсь CKModifyRecordsOperation чтобы сохранить записи для нескольких таблиц в Зоне по умолчанию для базы данных частного облака, всегда возвращается ошибка, кроме таблицы "X":

Ошибка сохранения записи на сервере: неверная попытка обновить запись с типа "X" на "Y"

error.userInfo деталь:

{
CKErrorDescription = "Ошибка сохранения записи CKRecordID: 0x7fd7a3d4c0c0; 1: (_ defaultZone:defaultOwner) на сервер: недопустимая попытка обновить запись с типа 'X' до 'Y'";
ContainerID = "iCloud.com...";
NSDebugDescription = "CKInternalErrorDomain: 2006";
NSLocalizedDescription = "Ошибка сохранения записи на сервере: недопустимая попытка обновить запись с типа 'X' до" Y "";
NSUnderlyingError = "CKError 0x7fa0d250c4e0: \" Недопустимые аргументы \" (2006); сообщение сервера = \" недопустимая попытка обновить запись с типа 'X' до 'Y'\"; uuid = E2E...D1E; ID контейнера = \ "iCloud.com... \" ";
RequestUUID = "E2E... D1E";
ServerErrorDescription = "недопустимая попытка обновить запись с типа 'X' до 'Y'";
errorKey = ck1rosofi;
}

Связанные фрагменты кода:

- (void)sync
{
  ...
  NSMutableArray * operations;
  for (NSString *tableName in @[@"X", @"Y"]) {
    CKModifyRecordsOperation * operation = [self _modifyRecordsOperationWithTableName:tableName];
    if (operation) {
      if (operations) [operations addObject:operation];
      else operations = [NSMutableArray arrayWithObject:operation];
    }
  }

  if (operations) {
    [operationQueue addOperations:operations waitUntilFinished:NO];
  }
}

- (CKModifyRecordsOperation *)_modifyRecordsOperationWithTableName:(NSString *)tableName
{
  ...

  NSMutableArray * recordsToSave = [NSMutableArray array];
  for (KYModel <KYModel_iCloudProtocol> *instance in unsyncedInstances) {
    CKRecordID * objectID = [[CKRecordID alloc] initWithRecordName:@(instance.id).stringValue];
    CKRecord * cloudRecord = [[CKRecord alloc] initWithRecordType:tableName recordID:objectID];
    ... setup record detail
    [recordsToSave addObject:record];
  }

  CKModifyRecordsOperation * operation = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:recordsToSave recordIDsToDelete:nil];
  operation.database = [[CKContainer defaultContainer] privateCloudDatabase];
  operation.savePolicy = CKRecordSaveAllKeys;
  operation.qualityOfService = NSQualityOfServiceUserInteractive;
  operation.atomic = NO;
  operation.perRecordProgressBlock = ...;
  operation.perRecordCompletionBlock = ^(CKRecord *record, NSError *error) {
    if (error) {
      // got error here
    }
    ...
  };
  operation.modifyRecordsCompletionBlock = ...;

  return operation;
}

1 ответ

Решение

После некоторого поиска и отладки я понял, что эти ошибочные записи имеют одно и то же "Имя записи" (это уникальное имя для CKRecordID, который любит уникальный идентификатор для каждой записи) уже в той же зоне, хотя они принадлежат к разным таблицам.


Решение первое:

Создание уникального CKRecordID "Имя записи" среди всех таблиц.

Например, добавьте префикс имени таблицы для каждого имени записи: [table_name]_[id].

Проблема моего случая выше заключается в том, что "Имя записи" дублируется для записей разных таблиц в одной зоне (Зона по умолчанию).

И с CKModifyRecordsOperation "s CKRecordSaveAllKeys сохраняя политику, при сохранении записи (id:1) для таблицы Y он находит другую запись (id:1) таблицы X в Cloud и пытается ее изменить, что выдает ошибку "недопустимая попытка обновить запись из типа" X ". 'к'Y'" наконец.


Решение второе:

Создание пользовательских зон для каждой таблицы (Примечание. Пользовательская зона доступна только для базы данных частного облака).

Например, вместо использования зоны по умолчанию, используйте клиентскую зону "X_Zone" для таблицы X и "Y_Zone" для таблицы Y. Затем мы можем продолжать использовать @"id" в качестве "Record Name" CKRecordID.

Таким образом, вы должны предоставить zoneID а также при преобразовании вашей локальной записи в экземпляр CKRecord:

CKRecordZoneID * zoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Custom_Zone_Name_Here"
                                                         ownerName:CKOwnerDefaultName];
CKRecordID * objectID = [[CKRecordID alloc] initWithRecordName:@(instance.id).stringValue zoneID:zoneID];
...

И, конечно же, сначала нужно создать соответствующую пользовательскую зону:

CKRecordZone * zone = [[CKRecordZone alloc] initWithZoneName:@"Custom_Zone_Name"];
[[[CKContainer defaultContainer] privateCloudDatabase] saveRecordZone:zone completionHandler:...];

или создать несколько одновременно:

NSMutableArray * zones = [NSMutableArray array];
for (NSString *zoneName in @[...]) {
  CKRecordZone * zone = [[CKRecordZone alloc] initWithZoneName:zoneName];
  [zones addObject:zone];
}
CKModifyRecordZonesOperation * operation = [[CKModifyRecordZonesOperation alloc] initWithRecordZonesToSave:zones recordZoneIDsToDelete:nil];
...
[[[CKContainer defaultContainer] privateCloudDatabase] addOperation:operation];

Я протестировал оба и работает как ожидалось.


Предложение:

Если ваши записи хранятся в базе данных частного облака, я думаю, что второе решение будет хорошим выбором, как сказал DOC:

... Используйте пользовательские зоны для организации и инкапсуляции групп связанных записей в частной базе данных. Пользовательские зоны также поддерживают другие возможности, такие как возможность записи нескольких записей в виде одной атомарной транзакции.

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

Будет гораздо удобнее обрабатывать записи для каждой таблицы, например, удаление зоны для таблицы вместо запроса и удаления записей для таблицы в той же зоне.

НО ПРИМЕЧАНИЕ, если вы используете CKReference НЕ используйте пользовательские зоны для этих таблиц, потому что

... класс CKReference не поддерживает связывание между зонами, поэтому каждый ссылочный объект должен указывать на запись в той же зоне, что и текущая запись.


Кстати, это ответ на вопрос с самоотвечением, надеюсь, это поможет, если кто-то другой получит такую ​​же проблему. Пожалуйста, укажите это, если что-то не так в моем ответе, и приветствуйте любые предложения, спасибо заранее.:)

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