let container = CKContainer.default()
let privateDB = container.privateCloudDatabase
let sharedDB = container.sharedCloudDatabase
// Use a consistent zone ID across the user's devices
// CKCurrentUserDefaultName specifies the current user's ID when creating a zone ID
let zoneID = CKRecordZoneID(zoneName: "Todos", ownerName: CKCurrentUserDefaultName)
// Store these to disk so that they persist across launches
var createdCustomZone = false
var subscribedToPrivateChanges = false
var subscribedToSharedChanges = false
let privateSubscriptionId = "private-changes"
let sharedSubscriptionId = "shared-changes"
创建自定义区域
let createZoneGroup = DispatchGroup()
if !self.createdCustomZone {
createZoneGroup.enter()
let customZone = CKRecordZone(zoneID: zoneID)
let createZoneOperation = CKModifyRecordZonesOperation(recordZonesToSave: [customZone], recordZoneIDsToDelete: [] )
createZoneOperation.modifyRecordZonesCompletionBlock = { (saved, deleted, error) in
if (error == nil) { self.createdCustomZone = true }
// else custom error handling
createZoneGroup.leave()
}
createZoneOperation.qualityOfService = .userInitiated
self.privateDB.add(createZoneOperation)
}
func fetchChanges(in databaseScope: CKDatabaseScope, completion: @escaping () -> Void) {
switch databaseScope {
case .private:
fetchDatabaseChanges(database: self.privateDB, databaseTokenKey: "private", completion: completion)
case .shared:
fetchDatabaseChanges(database: self.sharedDB, databaseTokenKey: "shared", completion: completion)
case .public:
fatalError()
}
}
func fetchDatabaseChanges(database: CKDatabase, databaseTokenKey: String, completion: @escaping () -> Void) {
var changedZoneIDs: [CKRecordZoneID] = []
let changeToken = … // Read change token from disk
let operation = CKFetchDatabaseChangesOperation(previousServerChangeToken: changeToken)
operation.recordZoneWithIDChangedBlock = { (zoneID) in
changedZoneIDs.append(zoneID)
}
operation.recordZoneWithIDWasDeletedBlock = { (zoneID) in
// Write this zone deletion to memory
}
operation.changeTokenUpdatedBlock = { (token) in
// Flush zone deletions for this database to disk
// Write this new database change token to memory
}
operation.fetchDatabaseChangesCompletionBlock = { (token, moreComing, error) in
if let error = error {
print("Error during fetch shared database changes operation", error)
completion()
return
}
// Flush zone deletions for this database to disk
// Write this new database change token to memory
self.fetchZoneChanges(database: database, databaseTokenKey: databaseTokenKey, zoneIDs: changedZoneIDs) {
// Flush in-memory database change token to disk
completion()
}
}
operation.qualityOfService = .userInitiated
database.add(operation)
}
创建和更新任何已更改的记录
删除不再存在的任何记录
更新区域更改令牌
以下是一些获取区域更改的示例代码:
func fetchZoneChanges(database: CKDatabase, databaseTokenKey: String, zoneIDs: [CKRecordZoneID], completion: @escaping () -> Void) {
// Look up the previous change token for each zone
var optionsByRecordZoneID = [CKRecordZoneID: CKFetchRecordZoneChangesOptions]()
for zoneID in zoneIDs {
let options = CKFetchRecordZoneChangesOptions()
options.previousServerChangeToken = … // Read change token from disk
optionsByRecordZoneID[zoneID] = options
}
let operation = CKFetchRecordZoneChangesOperation(recordZoneIDs: zoneIDs, optionsByRecordZoneID: optionsByRecordZoneID)
operation.recordChangedBlock = { (record) in
print("Record changed:", record)
// Write this record change to memory
}
operation.recordWithIDWasDeletedBlock = { (recordId) in
print("Record deleted:", recordId)
// Write this record deletion to memory
}
operation.recordZoneChangeTokensUpdatedBlock = { (zoneId, token, data) in
// Flush record changes and deletions for this zone to disk
// Write this new zone change token to disk
}
operation.recordZoneFetchCompletionBlock = { (zoneId, changeToken, _, _, error) in
if let error = error {
print("Error fetching zone changes for \(databaseTokenKey) database:", error)
return
}
// Flush record changes and deletions for this zone to disk
// Write this new zone change token to disk
}
operation.fetchRecordZoneChangesCompletionBlock = { (error) in
if let error = error {
print("Error fetching zone changes for \(databaseTokenKey) database:", error)
}
completion()
}
database.add(operation)
}
上面的代码有几条关于将更改写入内存然后将这些更改刷新到磁盘的注释。一般流程如下。
对于数据库:
当被告知区域删除时,将其写入内存。
当被告知数据库的新变更令牌时,将该令牌写入内存,然后:
将所有内存中区域更改保留到磁盘(删除已删除的区域,并记录需要更改的区域列表),或
将区域删除保留到磁盘,并获取所有已修改记录区域的更改。
注意: 在获取数据库更改时,需要在获取数据库更改令牌之前保留所有收到的每个区域回调。
最后,将更新的数据库更改标记刷新到磁盘
同样,对于区域:
当被告知区域中的记录更改时,请将其写入内存。
当被告知区域的新变更令牌时,将该区域中的所有内存中记录更改以及该区域的更新变更令牌提交到磁盘。
存储记录元数据
以下是您的应用如何读取元数据以便在本地存储它的示例:
// obtain the metadata from the CKRecord
let data = NSMutableData()
let coder = NSKeyedArchiver.init(forWritingWith: data)
coder.requiresSecureCoding = true
record.encodeSystemFields(with: coder)
coder.finishEncoding()
// store this metadata on your local object
yourLocalObject.encodedSystemFields = data
// set up the CKRecord with its metadata
let coder = NSKeyedUnarchiver(forReadingWith: yourLocalObject.encodedSystemFields!)
coder.requiresSecureCoding = true
let record = CKRecord(coder: coder)
coder.finishDecoding()
// write your custom fields...