正确使用后台获取完成处理程序
Posted
技术标签:
【中文标题】正确使用后台获取完成处理程序【英文标题】:Correct use of background fetch completion handler 【发布时间】: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
并确保它在您实际保存令牌之前成功。在这两个地方都这样做你会得到一个令牌。
是的,您对完成处理程序的想法听起来是正确的。
【参考方案1】:
更新您的getZoneChanges
以获得完成参数。
func getZoneChanges(completion: @escaping (Bool) -> Void)
// the rest of your code
operation.fetchRecordZoneChangesCompletionBlock = (error) in
if let error = error
print("Error fetching zone changes: \(error.localizedDescription)")
completion(false)
else
print("Changes fetched successfully!")
completion(true)
然后你可以更新应用委托方法来使用它:
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 (success) in
completionHandler(success ? .newData : .noData)
【讨论】:
以上是关于正确使用后台获取完成处理程序的主要内容,如果未能解决你的问题,请参考以下文章
使用 UILocalNotifications、带有完成处理程序的远程通知和后台获取
如何使用 swift 和 firebase 正确使用完成处理程序?