2

複数のテーブルのレコードをプライベート クラウド データベースのデフォルト ゾーンに保存するために使用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...

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;
}
4

1 に答える 1

3

しばらく検索してデバッグした後、それらの失敗したレコードは同じ「レコード名」(レコードCKRecordIDごとに一意の ID を好む の一意の名前) が既に同じゾーンにあることに気付きましたが、それらは異なるテーブルに属しています。 .


解決策 1:

CKRecordID の「レコード名」をすべてのテーブルの中で一意にする

たとえば、各レコード名にテーブル名のプレフィックスを追加します: [table_name]_[id]。

上記の私の場合の問題は、同じゾーン (デフォルト ゾーン) 内の異なるテーブルのレコードに対して「レコード名」が重複していることです。

の保存ポリシーでは、テーブル Y のレコード (id:1) を保存すると、クラウドでテーブル X の別のレコード (id:1) が見つかり、それを変更しようとすると、「レコードを更新しようとして無効です」というエラーがスローされCKModifyRecordsOperationます。タイプ「X」から「Y」へ」ついに。CKRecordSaveAllKeys


解決策 2:

各テーブルのカスタム ゾーンの作成(注: カスタム ゾーンはプライベート クラウド データベースでのみ使用できます)。

たとえば、デフォルト ゾーンを使用する代わりに、テーブル X にはカスタム ゾーン「X_Zone」を使用し、テーブル Y には「Y_Zone」を使用します。その後、@"id" を 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 が述べているように、ソリューション 2 が適切な選択になると思います。

... カスタム ゾーンを使用して、プライベート データベース内の関連するレコードのグループを整理およびカプセル化します。カスタム ゾーンは、複数のレコードを単一のアトミック トランザクションとして書き込む機能など、他の機能もサポートします。

各カスタム ゾーンは、データベース内の他のすべてのゾーンとは別の単一のデータ ユニットとして扱います。ゾーン内では、他の場所と同じようにレコードを追加します。...

同じゾーン内のテーブルのレコードをクエリして削除する代わりに、テーブルのゾーンを削除するなど、各テーブルのレコードを処理する方がはるかに便利です。

ただし、テーブル間で使用する場合はCKReference、それらのテーブルにカスタム ゾーンを使用しないでください。

... CKReference クラスはクロスゾーン リンクをサポートしていないため、各参照オブジェクトは現在のレコードと同じゾーン内のレコードを指す必要があります。


ところで、これは Self-Answered-Question の回答です。他の誰かが同じ問題を抱えている場合に役立つことを願っています。私の答えに何か問題がある場合は指摘してください。また、事前に任意の提案も歓迎します。:)

于 2016-05-17T13:44:22.267 に答える