2014-09-12 10 views
8

Próbuję przesłać niektóre zmiany rekordów z CloudKit. Mam zamiar użyć CKModifyRecordsOperation do załadowania wsadowego rekordów, które zmieniły się na urządzeniu. Zapisy są w strefie niestandardowegoCloudKit CKModifyRecordsOperation daje mi "Dane ochrony nie pasują"

Z jakiegoś powodu operacja uporczywie powraca z błędem mówi mi „” Ochrony danych nie pasuje”

Oto kod:

- (void)updloadLocalChangesWithCompletionBlock:(void (^)(NSError *error))completionBlock 
{ 
    // Initialize the data 
    NSArray *localChanges = self.localChanges; 
    NSArray *localDeletions = self.localDeletions; 

    // Initialize the database and modify records operation 
    CKDatabase *database = [CKContainer defaultContainer].privateCloudDatabase; 
    CKModifyRecordsOperation *modifyRecordsOperation = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:localChanges recordIDsToDelete:localDeletions]; 
    modifyRecordsOperation.savePolicy = CKRecordSaveAllKeys; 

    NSLog(@"CLOUDKIT Changes Uploading: %d", localChanges.count); 

    // Add the completion block 
    modifyRecordsOperation.modifyRecordsCompletionBlock = ^(NSArray *savedRecords, NSArray *deletedRecordIDs, NSError *error) { 
     if (error) { 
      NSLog(@"[%@] Error pushing local data: %@", self.class, error); 
     } 

     [self.localChanges removeObjectsInArray:savedRecords]; 
     [self.localDeletions removeObjectsInArray:deletedRecordIDs]; 

     completionBlock(error); 
    }; 

    // Start the operation 
    [database addOperation:modifyRecordsOperation]; 
} 

Tutaj jest błąd to daje mi:

[CloudKitSyncManager] Error Uploading Changes: <CKError 0x156654a0: "Partial Failure" (2/1011); "Failed to modify some records"; partial errors: { 
    default-00001:(ZoneName:UserRecordID) = <CKError 0x1550b5a0: "Server Record Changed" (14/2037); "Error saving record <CKRecordID: 0x18080430; default-00001:(ZoneName:UserRecordID)> to server: Protection data didn't match"> 
}> 

Odpowiedz

5

Czym dokładnie jest w swojej tablicy self.localChanges? CKRecord Zakładam, ale czy zmodyfikowane rekordy przesyłane są na podstawie tych samych obiektów CKRecord, które pobrano z CloudKit?

Miałem podobny (choć nie dokładny) komunikat o błędzie, gdy próbowałem załadować świeżo przydzieloną CKRecord przy moich lokalnych zmianach i oczekiwałem, że nadpisze kopię serwera. Naprawiłem to, pobierając zdalną kopię mojego produktu, stosując aktualizacje do instancji CKRecord przekazanej mi przez CloudKit, a następnie przesyłając ją.

+0

Tak, to były CKRecords i tak, używałem świeżych CKRecords do nadpisywania istniejących i tak, twoje rozwiązanie działało! W dokumentach nie było to jednoznaczne. No cóż. Dziękuję za odpowiedź! – Jonathan

2

powinieneś zestaw prawa zapisu dla odpowiednich typów rekordów w dashboa tak myślę.

enter image description here

+0

Nie myślałem, żeby to zrobić. Po prostu zrobiłem to i nie zadziałało. – Jonathan

0

Warto zauważyć, że rozwiązanie tego problemu było dwojakie. Próbowałem też użyć strefy niestandardowej. Gdziekolwiek byłem inicjowanie CKRecordZoneID kod wyglądał tak:

[[CKRecordZoneID alloc] initWithZoneName:@"CustomZoneName" ownerName:self.userRecordId.recordName]; 

Powinno to wyglądało to

[[CKRecordZoneID alloc] initWithZoneName:@"CustomZoneName" ownerName:CKOwnerDefaultName]; 

wierzę, że jest to część tego, co było przyczyną „Ochrona danych nie pasuje Bug ", ale odpowiedź Dave'a Teare'a była również konieczna, aby temu zaradzić.

7

Jeśli używasz savePolicy od CKRecordSaveIfServerRecordUnchanged, konieczne jest pre-fetch zdalnego CKRecord (a zwłaszcza aktualizacji że instancja) przed CKModifyRecordsOperation. Jeśli przydzielisz "lokalny" numer CKRecord i uzupełnisz go poprzez CKRecordID z pasującym initWithRecordName:, CloudKit nie może porównać zdalnego znacznika change z (brakującym) lokalnym znacznikiem zmiany, więc nie powiedzie się z CKErrorServerRecordChanged i nieco niejasnym komunikatem o błędzie: "dane ochrony nie pasują".

Jeśli jednak zdalny CKRecord w ogóle nie istnieje, z pewnością rekord serwera nie został zmieniony i operacja zapisywania może być kontynuowana. To zachowanie jest podstawą użycia "Save If Not Exists" (SQL: INSERT nad kluczem podstawowym). Jeśli przydzielisz "lokalny" CKRecord i użyjesz savePolicy z CKRecordSaveIfServerRecordUnchanged, to INSERT, ale nigdy NIE ZAKTUALIZUJ.

Jeśli używasz savePolicy z CKRecordSaveAllKeys i CKRecordZoneIDownerName z CKOwnerDefaultName, powinieneś być w stanie zaoszczędzić (SQL: UPDATE, INSERT jeśli to konieczne) poprzez "lokalnie" przyznane CKRecord, oszczędzając (pre-fetch) podróż przez sieć.

W przypadku użycia instrukcji "Save If Exists" (SQL: UPDATE na kluczu podstawowym) prawdopodobnie nie można wykonać bez (weryfikacji) wyłączenia przez sieć.

Zaczep: nie jest to możliwe do użycia zarówno CKRecordSaveIfServerRecordUnchanged wkładką a CKRecordSaveAllKeys Update w jednej operacji atomowej od transakcji CloudKit obejmować tylko pojedynczą CKModifyRecordsOperation, pomiędzy wieloma CKRecord/CKRecordID przykłady. Filozoficznie rzecz biorąc, "transakcja" powinna obejmować wiele "operacji" (zapytania SQL), a nie tylko wiele "rekordów" (SQL: wiersze).

Powiązane problemy