CloudKit: ошибка сохранения записи с помощью CKModifyRecordsOperation

<CKError 0x14d8cb70: "Partial Failure" (2/1011); "Failed to modify some records"; partial errors: {
    B5DEF0B5-F064-4B27-9C89-BE75C9134297:(_defaultZone:__defaultOwner__) = <CKError 0x14d83b70: "Server Record Changed" (14/2037); "Error saving record <CKRecordID: 0x15748cd0; B5DEF0B5-F064-4B27-9C89-BE75C9134297:(_defaultZone:__defaultOwner__)> to server: Protection data didn't match">
}>

Я получаю эту ошибку, когда пытаюсь сохранить CKRecords до CloudKit. Есть идеи?

Должен ли я получать первые записи, как в удобном API?

Я использую метод CKModifyRecordsOperation, чтобы обновить больше записей.


person János    schedule 14.04.2015    source источник


Ответы (1)


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

Если версия записи на сервере новее, чем версия, которую вы пытались сохранить, сервер возвращает ошибку CKErrorServerRecordChanged. Словарь userInfo объекта ошибки содержит разные версии конфликтующих записей. Используйте эти ключи для извлечения записей и выполнения любой логики разрешения, необходимой для разрешения конфликта.

Как описано ниже, в вашем случае проблема заключалась в том, что вы сохранили объект в базе данных и воссоздали CKRecord для внесения изменений. В этом случае вам необходимо сохранить системные поля, используя CKRecord encodeSystemFieldsWithCoder. И воссоздайте CKRecord, инициализировав его с помощью NSCoder. Вы можете использовать NSKeyedArchiver и NSKeyedUnarchiver для сохранения CKRecord и его воссоздания. Если вам нужен образец, см. методы fromCKRecord и toCKRecord в нижней части https://github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/AppMessage/CloudKit/EVCloudKitDao.swift

person Edwin Vermeer    schedule 14.04.2015
comment
для 1 записи нормально, но для обновления 100 записей, это серьезно, я должен сначала скачать, найти, какую из них изменить, и загрузить ?? брбрбр - person János; 14.04.2015
comment
Вам не нужно читать их снова. см. мой обновленный ответ. Так что просто сохраните их все и повторите попытку обновления, когда вы получите сообщение об ошибке, используя идентификатор записи внутри ошибки. - person Edwin Vermeer; 14.04.2015
comment
Вы можете использовать -[CKRecord encodeSystemFieldsWithCoder:] для кодирования только системных полей (например, тега изменения) для записи. Позже вы можете разархивировать запись только с системными полями, применить только те изменения, которые вы хотите сохранить на сервере, и сохранить запись. Если вы уже ведете локальную базу данных записей, это избавит вас от необходимости обращаться к серверу туда и обратно. - person farktronix; 15.04.2015
comment
Я делаю, как вы говорите, (1) читаю (2) изменяю CKRecords (3) сохраняю, и делаю это с определенным набором записи довольно часто, а иногда я получаю только часть записей, а не все по тому условию, которое я указал в предикате CKQuery, знаете почему? - person János; 15.04.2015
comment
Результат, возвращаемый CloudKit, ограничен. Стандартное количество записей — 100, но оно может варьироваться в зависимости от общей нагрузки на iCloud. Если вы используете SKQueryOperation, вы можете повлиять на количество возвращаемых записей, установив .resultsLimit. Значение по умолчанию для этого — CKQueryOperationMaximumResults. - person Edwin Vermeer; 15.04.2015
comment
Я часто обновляю максимум 5 записей, я не думаю, что это проблема ограничения ... но может ли быть так, что даже сохранение завершено на клиенте, а следующая итерация чтения может и начнется сразу после Сохраняя завершение, CloudKit не полагался на завершение операции?! - person János; 15.04.2015
comment
Поведение CKModifyRecordsOperation по умолчанию является атомарным. (см. атомарное свойство). Поэтому, если одно обновление завершается ошибкой, все должны завершиться ошибкой. В тот момент, когда записи записаны и результат был в порядке, любой запрос после этого должен включать эти измененные записи. Но эй... Я видел странные вещи/ошибки в CloudKit. Поэтому, если вы уверены, что с вашим кодом все в порядке, но все еще часто не получаете полного ответа, вы должны отправить Radar - person Edwin Vermeer; 15.04.2015
comment
Вы должны использовать NSKeyedArchiver и NSKeyedUnarchiver для сохранения CKRecord и его воссоздания. Если вам нужен образец, см. методы fromCKRecord и toCKRecord в нижней части github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/ - person Edwin Vermeer; 15.04.2015
comment
Я не думаю, что Apple хочет, чтобы мы архивировали CKRecord:s как есть, или я что-то упускаю. Насколько я знаю, они хотят, чтобы мы использовали свои собственные структуры данных. Насколько я понимаю, это неявно означает, что мы не должны сохранять/кешировать CKRecord:s (только системные поля, использующие encodeSystemFieldsWithCoder). - person Jonny; 02.11.2016
comment
В какой степени использование свойства желаемых ключей CKQueryOperation уменьшит проблема обновления сотен записей? - person AmitaiB; 03.03.2017
comment
@AmitaiB Даже если вы обновите только 1 поле, запись будет обновлена. Токен изменения предназначен для всей записи. Таким образом, если вы извлекаете только половину полей, а обновление находится в других полях, то запись по-прежнему считается обновленной, и вам придется выполнить повторное чтение, даже если изменение не в ваших полях. - person Edwin Vermeer; 03.03.2017
comment
Таким образом, это сэкономит место (пропускную способность, память, хранилище), но не время. Спасибо - person AmitaiB; 03.03.2017