Как (и когда) использовать метод iCloud encodeSystemFields в CKRecord?

encodeSystemFields предполагается использовать, когда я веду записи локально в базе данных.

Должен ли я делать что-то особенное при десериализации после экспорта этих данных?

В каких сценариях я должен действовать в соответствии с информацией в этих данных?

Как вариант (и если он не описан в предыдущем вопросе), от чего мне помогает эта информация? (я полагаю, повреждение данных)


person halfbit    schedule 28.09.2016    source источник


Ответы (1)


encodeSystemFields полезен, чтобы избежать повторного получения CKRecord из CloudKit для его обновления (исключение конфликтов записей).

Идея такая:

Когда вы сохраняете данные для записи, полученной из CloudKit (например, полученной через CKFetchRecordZoneChangesOperation для синхронизации изменений записей с локальным хранилищем):

1.) Заархивируйте CKRecord в NSData:

let record = ...

// archive CKRecord to NSData
let archivedData = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWithMutableData: archivedData)
archiver.requiresSecureCoding = true
record.encodeSystemFieldsWithCoder(with: archiver)
archiver.finishEncoding()

2.) Храните архивные данные локально (например, в вашей базе данных), связанные с вашей локальной записью.

Если вы хотите сохранить изменения, внесенные в локальную запись, обратно в CloudKit:

1.) Разархивируйте CKRecord из сохраненных вами NSData:

let archivedData = ... // TODO: retrieved from your local store

// unarchive CKRecord from NSData
let unarchiver = NSKeyedUnarchiver(forReadingWithData: archivedData)  
unarchiver.requiresSecureCoding = true 
let record = CKRecord(coder: unarchiver)

2.) Используйте эту неархивированную запись как основу для ваших изменений. (т.е. установить на нем измененные значения)

record["City"] = "newCity"

3.) Сохраните записи в CloudKit с помощью CKModifyRecordsOperation.


Почему?

От Apple:

Хранение записей локально

Если вы храните записи в локальной базе данных, используйте метод encodeSystemFields (with :) для кодирования и хранения метаданных записи. Метаданные содержат идентификатор записи и тег изменения, которые понадобятся позже для синхронизации записей в локальной базе данных с записями, хранящимися в CloudKit.

Когда вы сохраняете изменения в CKRecord в CloudKit, вам необходимо сохранить изменения в записи сервера.

Вы не можете просто создать новый CKRecord с тем же идентификатором записи, установить для него значения и сохранить его. Если вы это сделаете, вы получите ошибку Server Record Changed - в данном случае это связано с тем, что существующая запись сервера содержит метаданные, которые отсутствуют в вашей локальной записи (созданной с нуля).

Итак, у вас есть два варианта решения этой проблемы:

  1. Запросите CKRecord у CloudKit (используя идентификатор записи), внесите изменения в этот CKRecord, а затем сохраните его обратно в CloudKit.

  2. Используйте encodeSystemFields и сохраните метаданные локально, разархивировав их, чтобы создать базовый CKRecord, содержащий все соответствующие метаданные для сохранения изменений в упомянутом CKRecord обратно в CloudKit.

# 2 спасает вас от сетевых циклов *.

* Предполагается, что другое устройство за это время не изменило запись - от чего также помогают защититься эти данные. Если другое устройство изменяет запись между моментом, когда вы в последний раз ее получили, и моментом, когда вы пытаетесь ее сохранить, CloudKit (по умолчанию) отклонит вашу попытку сохранения записи с измененной записью сервера. Это ваш ключ к разрешению конфликтов способом, который подходит для вашего приложения и модели данных. (Часто путем получения новой записи сервера из CloudKit и повторного применения соответствующих изменений значений к этой записи CKRecord перед повторной попыткой сохранения.)

ПРИМЕЧАНИЕ. Каждый раз, когда вы сохраняете / извлекаете обновленный CKRecord в / из CloudKit, вы должны не забывать обновлять локально сохраненный заархивированный CKRecord.

person breakingobstacles    schedule 28.09.2016
comment
Устраняет ли encodeSystemFields необходимость сериализации всей записи в моей БД? Или, если бы мне пришлось хранить все в MySQL, в том числе и эти сериализованные данные, есть ли шанс, что я тоже могу быть избыточным и расточительным? - person halfbit; 03.10.2016
comment
@LamonteCristo: encodeSystemFields только кодирует значения системных метаданных CKRecord, не какие-либо установленные вами ключи + значения. Таким образом, вы обязательно должны записывать данные (поля, которые вы устанавливаете) из CKRecord в свою БД отдельно - это не избыточно. - person breakingobstacles; 10.02.2017
comment
Это было очень полезно. Я не могу тебя отблагодарить, @breakingobstacles - person Clifton Labrum; 26.06.2018
comment
Как вы инициализируете новый CKRecord в своем приложении, если encodeSystemFields будет nil для запуска? Можете ли вы создать такую ​​запись вручную? let record = CKRecord(recordType: "...", recordID: CKRecordID(recordName: "...", zoneID: "...")) - person Clifton Labrum; 26.06.2018
comment
@CliftonLabrum Да, при создании новых записей, которые еще не были сохранены в iCloud, это именно то, что вы делаете. Затем вы передаете новую запись CKModifyRecordsOperation, чтобы сохранить ее. Если вы установите perRecordCompletionBlock или modifyRecordsCompletionBlock для операции, тогда у вас будет доступ к системным полям, которые сервер установил для новой записи. - person sc0rp10n; 13.01.2020