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

Correct use of background fetch completion handler

我的应用程序使用 CloudKit,我正在尝试实施后台提取。

App Delegate 中的方法调用我的主视图控制器中的一个方法,该方法检查 CloudKit 数据库中的更改。

但是,我意识到我没有正确调用完成处理程序,因为 CloudKit 的闭包将 return 异步。我真的不确定操作完成后如何最好地调用应用程序委托方法中的完成处理程序。我可以将完成处理程序传递给视图控制器方法吗?

应用代理

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 以获得完成参数。

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)
        }
    }
}