18

encodeSystemFields当我在数据库中本地保存记录时应该使用它。

导出该数据后,在反序列化时是否必须做一些特别的事情?

我应该根据该数据中的信息采取哪些行动?

作为一种变体(如果上一个问题没有涉及),这些信息可以帮助我防范什么?(我假设数据损坏)

4

2 回答 2

39

encodeSystemFields 有助于避免再次从 CloudKit 获取 CKRecord 来更新它(除非记录冲突)。

这个想法是:

当您存储从 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.) 将archivedData 存储在本地(例如,在您的数据库中)与您的本地记录相关联。

当您要将对本地记录所做的更改保存回 CloudKit 时

1.) 从您存储的 NSData 中取消归档 CKRecord:

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.) 通过 CKModifyRecordsOperation 将记录保存到 CloudKit。


为什么?

来自苹果:

本地存储记录

如果您将记录存储在本地数据库中,请使用 encodeSystemFields(with:) 方法对记录的元数据进行编码和存储。元数据包含记录 ID 和更改标记,稍后需要将本地数据库中的记录与 CloudKit 存储的记录同步。

当您在 CloudKit 中保存对 CKRecord 的更改时,您需要将更改保存到服务器的记录中。

您不能只创建一个具有相同 recordID 的新 CKRecord,在其上设置值并保存它。如果这样做,您将收到“服务器记录已更改”错误 - 在这种情况下,这是因为现有服务器记录包含您的本地记录(从头开始创建)丢失的元数据。

所以你有两种选择来解决这个问题:

  1. 从 CloudKit 请求 CKRecord(使用 recordID),对该 CKRecord 进行更改,然后将其保存回 CloudKit。

  2. 使用encodeSystemFields并在本地存储元数据,将其解压缩以创建一个“基本”CKRecord,该 CKRecord 具有所有适当的元数据,用于将所述 CKRecord 的更改保存回 CloudKit。

#2 为您节省网络往返*。

*假设另一台设备在此期间没有修改记录——这也是这些数据可以帮助您防范的。如果另一台设备在您上次检索到您尝试保存它的时间之间修改了记录,CloudKit 将(默认情况下)拒绝您的记录保存尝试,并显示“服务器记录已更改”。这是您以适合您的应用程序和数据模型的方式执行冲突解决的线索。(通常,通过从 CloudKit 获取新的服务器记录,并在再次尝试保存之前将适当的值更改重新应用到该 CKRecord。)

注意:每当您将更新的 CKRecord 保存到 CloudKit 或从 CloudKit 检索时,您必须记住更新本地存储的存档 CKRecord。

于 2016-09-28T04:22:50.450 回答
0

从 iOS 15 / Swift 5.5 开始,这个扩展可能会有所帮助:

public extension CKRecord {
    var systemFieldsData: Data {
        let archiver = NSKeyedArchiver(requiringSecureCoding: true)
        encodeSystemFields(with: archiver)
        archiver.finishEncoding()
        return archiver.encodedData
    }

    convenience init?(systemFieldsData: Data) {
        guard let una = try? NSKeyedUnarchiver(forReadingFrom: systemFieldsData) else {
            return nil
        }
        self.init(coder: una)
    }
}
于 2021-11-03T16:02:39.203 回答