【问题标题】: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 并确保它在您实际保存令牌之前成功。在这两个地方都这样做你会得到一个令牌。
  • 是的,您对完成处理程序的想法听起来是正确的。

标签: ios swift cloudkit completionhandler background-fetch


【解决方案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)
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多