如何(以及何时)在CKRecord上使用iCloud的encodeSystemFields方法?

21

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

一旦我导出数据,在反序列化时是否需要采取任何特殊措施?

在什么情况下应该根据该数据中的信息采取行动?

作为一种变化(如果前一个问题未涉及),这些信息有什么帮助可以防范什么?(我认为是数据损坏)

2个回答

43

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。


1
encodeSystemFields是否可以避免我在数据库中序列化整个记录?如果我将所有内容存储在MySQL中,包括这些序列化数据,那么我是否可能会变得冗余和浪费? - makerofthings7
3
encodeSystemFields方法仅编码CKRecord的系统元数据值,而不是您设置的任何键和值。因此,您绝对应该将CKRecord中的“数据”(您设置的字段)单独记录在数据库中 - 这不是冗余的。请注意,不能改变原意。 - breakingobstacles
这非常有帮助。我无法感谢你足够,@breakingobstacles - Clifton Labrum
1
你如何在应用程序中初始化一个全新的 CKRecord,因为 encodeSystemFields 将从 nil 开始?你可以像这样手动创建记录吗?let record = CKRecord(recordType: "...", recordID: CKRecordID(recordName: "...", zoneID: "...")) - Clifton Labrum
@CliftonLabrum 是的,当创建尚未保存到iCloud的新记录时,这正是您要做的。然后将新记录传递给CKModifyRecordsOperation以保存它。如果在操作上设置了perRecordCompletionBlockmodifyRecordsCompletionBlock,则您将可以访问服务器在新记录上设置的系统字段。 - sc0rp10n

4

从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)
    }
}

1
从Swift 5.8开始,原始答案会发出弃用警告,现在这个答案是正确的,并且向后兼容使用原始答案生成的数据。非常感谢Klaas。 - Kem Mason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接