正确使用后台获取完成处理程序

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 = changeTokenself.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 正确使用完成处理程序?

获取打印机后台处理程序名称和连接类型

将应用程序推送到后台时,NSManagedObjectContext 不会获取或保存对象

javascript 怎样获取配置文件中的参数

如何获取后台进程的进程ID?