【发布时间】:2019-09-03 19:33:43
【问题描述】:
我的应用使用 CloudKit,我正在尝试实现后台提取。
App Delegate 中的方法调用我的主视图控制器中的一个方法,该方法检查 CloudKit 数据库中的更改。
但是,我意识到我没有正确调用完成处理程序,因为 CloudKit 的闭包将异步返回。一旦操作完成,我真的不确定如何最好地在应用程序委托方法中调用完成处理程序。我可以将完成处理程序传递给视图控制器方法吗?
应用代理
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
// Code to get a reference to main view controller
destinationViewController.getZoneChanges()
completionHandler(.newData)
}
}
获取 CloudKit 更改的主视图控制器方法
// Fetch zone changes (a method in main table view controller)
func getZoneChanges() {
DispatchQueue.global(qos: .userInitiated).async {
let customZone = CKRecordZone(zoneName: "Drugs")
let zoneID = customZone.zoneID
let zoneIDs = [zoneID]
let changeToken = UserDefaults.standard.serverChangeToken // Custom way of accessing User Defaults using an extension
// Look up the previous change token for each zone
var optionsByRecordZoneID = [CKRecordZone.ID: CKFetchRecordZoneChangesOperation.ZoneOptions]()
// Some other functioning code to process options
// CK Zone Changes Operation
let operation = CKFetchRecordZoneChangesOperation(recordZoneIDs: zoneIDs, optionsByRecordZoneID: optionsByRecordZoneID)
// Closures for records changed, deleted etc.
// Closure details omitted for brevity as fully functional as expected.
// These closures change data model, Spotlight indexing, notifications and trigger UI refresh etc.
operation.recordChangedBlock = { (record) in
// Code...
}
operation.recordWithIDWasDeletedBlock = { (recordId, string) in
// Code...
}
operation.recordZoneChangeTokensUpdatedBlock = { (zoneId, token, data) in
UserDefaults.standard.serverChangeToken = changeToken
UserDefaults.standard.synchronize()
}
operation.recordZoneFetchCompletionBlock = { (zoneId, changeToken, _, _, error) in
if let error = error {
print("Error fetching zone changes: \(error.localizedDescription)")
}
UserDefaults.standard.serverChangeToken = changeToken
UserDefaults.standard.synchronize()
}
operation.fetchRecordZoneChangesCompletionBlock = { (error) in
if let error = error {
print("Error fetching zone changes: \(error.localizedDescription)")
} else {
print("Changes fetched successfully!")
// Save local items
self.saveData() // Uses NSCoding
}
}
CKContainer.default().privateCloudDatabase.add(operation)
}
}
【问题讨论】:
-
让您的
getZoneChanges方法采用完成处理程序。 -
与您的问题无关,但您的 CloudKit 代码存在严重缺陷。在成功处理相关更改之前,您不应保存区域的更改令牌。想象一下,如果您的应用程序在调用
UserDefaults.standard.serverChangeToken = changeToken和self.saveData()之间被终止或崩溃会发生什么。那些排队(和未保存)的更改将永远丢失。 -
@rmaddy 感谢您的建议。那么我可以从最终的 CK 操作闭包中调用完成处理程序吗?我假设完成处理程序会依次调用后台获取完成处理程序?
-
您保存令牌的代码很好。但是您应该首先调用
saveData并确保它在您实际保存令牌之前成功。在这两个地方都这样做你会得到一个令牌。 -
是的,您对完成处理程序的想法听起来是正确的。
标签: ios swift cloudkit completionhandler background-fetch