Write data to Firebase in the background after retrieving steps with HealthKit's background delivery

匆匆过客 提交于 2019-12-08 06:45:09

问题


I have an HKObserverQuery setup to fetch steps in the background (enableBackgroundDelivery method is called in application:didFinishLaunchingWithOptions:).

The steps are retrieved in the background, but I would also like to store the retrieved steps in a Firebase database. This part is failing though, nothing is stored in Firebase. The method to store the steps does work correctly when the app is in the foreground. Any ideas on how to successfully write data in Firebase while in the background would be appreciated.

class HealthKitManager {

    static let shared = HealthKitManager()
    private let healthStore = HKHealthStore()
    private let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!

    private init() {

    }

    func getTodaysStepCount(completion: @escaping (Double) -> Void) {
        let now = Date()
        let startOfDay = Calendar.current.startOfDay(for: now)
        let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: now, options: .strictStartDate)

        let query = HKStatisticsQuery(quantityType: stepsQuantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { (_, result, error) in
            var resultCount = 0.0

            guard let result = result else {
                log.error("Failed to fetch steps = \(error?.localizedDescription ?? "N/A")")
                completion(resultCount)
                return
            }

            if let sum = result.sumQuantity() {
                resultCount = sum.doubleValue(for: HKUnit.count())
            }

            DispatchQueue.main.async {
                completion(resultCount)
            }
        }

        healthStore.execute(query)
    }

    func enableBackgroundDelivery() {
        let query = HKObserverQuery(sampleType: stepsQuantityType, predicate: nil) { [weak self] (query, completionHandler, error) in
            if let error = error {
                log.error("Observer query failed = \(error.localizedDescription)")
                return
            }

            self?.getTodaysStepCount(completion: { steps in
                // Store steps using Firebase:
                StepsManager.shared.updateUserSteps(steps)
                completionHandler()
            })
        }

        healthStore.execute(query)
        healthStore.enableBackgroundDelivery(for: stepsQuantityType, frequency: .hourly) { (success, error) in
            log.debug("Background delivery of steps. Success = \(success)")

            if let error = error {
                log.error("Background delivery of steps failed = \(error.localizedDescription)")
            }
        }
    }

}

回答1:


Ok, I solved this. The problem was that I was calling the completionHandler, telling HealthKit that I was done with my operation, before the saving to Firebase was actually completed. Saving to Firebase is done asynchronously.

I added a completion handler to StepsManager.shared.updateUserSteps function:

func updateUserSteps(_ steps: Double, withCompletion completion: (() -> Void)? = nil) {
    let stepsReference = databaseInstance.reference(withPath: stepsPath)
    stepsReference.setValue(steps) { (error, _) in
        if let completion = completion {
            completion()
        }
    }
}

which is triggered when the databaseRef.setValue has completed. I then updated the observer query to the following:

self?.getTodaysStepCount(completion: { steps in
    StepsManager.shared.updateUserSteps(steps) {
        completionHandler()
    }
})

The Firebase operation completes correctly now.



来源:https://stackoverflow.com/questions/44241093/write-data-to-firebase-in-the-background-after-retrieving-steps-with-healthkits

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!