ios - 将记录CKRecordID保存到服务器时出错:无效尝试将记录从类型“X”更新为“Y”

标签 ios objective-c cloudkit ckmodifyrecordsoperation ckerror

当我使用CKModifyRecordsOperation将多个表的记录保存到 private 云数据库的默认区域时,它总是返回以下错误,但表'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;
}

最佳答案

经过一段时间的搜索和调试后,我意识到那些失败的记录已经在相同的区域中了,尽管它们属于不同的记录,但它们具有相同的“记录名称”(这是CKRecordID的唯一名称,就像每个记录的唯一ID)。表。

解决方案一:

使CKRecordID的“记录名称”在所有表中都是唯一的

例如,为每个记录名添加一个表名前缀:[table_name] _ [id]。

我上面的案例的问题是,对于同一区域(默认区域)中不同表的记录,“记录名称”重复了。

并且使用CKModifyRecordsOperationCKRecordSaveAllKeys保存策略,当保存表Y的记录(id:1)时,它会在Cloud上找到表X的另一条记录(id:1),并尝试对其进行修改,这将引发错误“无效尝试以最后将记录从类型“X”更新为“Y””。

解决方案二:

为每个表创建自定义区域(注意:“自定义区域”仅适用于 private 云数据库)。

例如,不要使用默认区域,而将客户区域“X_Zone”用于表X,将“Y_Zone”用于表Y。然后,我们可以继续使用@“id”作为CKRecordID的“记录名称”。

这样,在将本地记录转换为CKRecord实例时,还需要提供zoneID:

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];

我已经测试了两者并按预期工作。

建议:

如果您的记录存储在 private 云数据库中,我认为解决方案二将是一个不错的选择,如DOC所述:

...使用自定义区域在 private 数据库中排列和封装相关记录组。自定义区域还支持其他功能,例如将多个记录作为单个原子事务写入的功能。

将每个自定义区域视为与数据库中其他所有区域分开的单个数据单元。在区域内,您可以像在其他任何地方一样添加记录。
...

处理每个表的记录将更加方便,例如删除表的区域而不是查询和删除同一区域中的表的记录。

但请注意,如果您在表之间使用CKReference,请不要对这些表使用自定义区域,原因是

... CKReference类不支持跨区域链接,因此每个引用对象必须指向与当前记录位于同一区域中的记录。


顺便说一句,这是“自我回答”问题的答案,希望它对其他人遇到同样的问题有所帮助。如果我的回答有误,请指出,并欢迎您提出任何建议。 :)

关于ios - 将记录CKRecordID保存到服务器时出错:无效尝试将记录从类型“X”更新为“Y”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37277962/

相关文章:

ios - UITableView configureCellForDisplay :forIndexPath: 中的断言失败

objective-c - 在其他 View 上显示 NSView 子类

ios - 应用因使用 CloudKit 而被拒绝

cloudkit - CKQuery 哪里谓词有引用而不是字段?

ios - UIPanGestureRecognizer 为什么以及如何使 UISwipeGestureRecognizer 静音,而 UITapGestureRecognizers 默认情况下不会相互静音?

objective-c - NSArray 如何实现 NSCopying 协议(protocol)

ios - 如何从 Objective C 类访问 swift 类的 List 属性

ios - Jenkins 构建中的未知断言失败

objective-c - 在 UIResponder 链上发送事件?

ios - CloudKit 不检索应该存在且可检索的记录