问题
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