I am building an app which is supposed to listen to changes in HealthKit
regarding newly added heart rate data using HKObserverQuery
but weirdly the observer's updateHandler
block doesn't fire consistently when I debug using breakpoints and console logs and adding new data manually to HealthKit
.
Here is most of the code:
import HealthKit
protocol HeartRateDataProviding {
var delegate: HeartRateDataProviderDelegate? { get set }
func startExecutingQuery(until: Date?)
}
protocol HeartRateDataProviderDelegate: class {
func didQueryData(entry: HeartRateDataEntry)
}
struct HeartRateDataEntry {
let date: Date
let value: Int
}
class HeartRateDataProvider: HeartRateDataProviding {
private let healthStore: HKHealthStore
private let heartRateType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
private let heartRateUnit = HKUnit(from: "count/min")
weak var delegate: HeartRateDataProviderDelegate?
init(healthStore: HKHealthStore = .init()) {
self.healthStore = healthStore
}
func startExecutingQuery() {
healthStore.enableBackgroundDelivery(for: heartRateType, frequency: .immediate) { success, error in
print("Observer Query background delivery enabled -> successful: \(success) error: \(String(describing: error))")
}
healthStore.execute(self.createObserverQuery())
}
private func createObserverQuery() -> HKQuery {
let query = HKObserverQuery(sampleType: heartRateType, predicate: nil) { query, completionHandler, error in
print("Hello!")
completionHandler()
}
return query
}
private func formatSamples(samples: [HKSample]?) {
guard let samples = samples as? [HKQuantitySample],
let sample = samples.last else { return }
let entry = HeartRateDataEntry(date: sample.endDate,
value: Int(sample.quantity.doubleValue(for: heartRateUnit)))
delegate?.didQueryData(entry: entry)
}
}
I have an instance of this class in my AppDelegate
and executing the query on didFinishLaunchingWithOptions
.
the query's updateHandler
block fires only once when I set it up and when the app comes to foreground, If I put the app in background and go to Health App and try adding new data, the behaviour is very inconsistent.
Interestingly the block fires correctly for about 5-6 times if I never call the completionHandler
like the Apple docs suggest it, if I call the completionHandler
then it works maybe once in every 5 tries.
I am planning to use HKAnchoredObjectQuery
to fetch the last data which I've tried and it works fine when the observer fires but I can't get the observer to work properly.
Am I missing something? Some help would be greatly appreciated.
Update: I've changed the data type to bloodGlucose
for testing purposes and noticed that updateHandler
fires correctly every time, this leads me to think that code I implemented is correct and the behaviour varies depending on the data type, can someone confirm this?
According to Apple documentation (https://developer.apple.com/documentation/healthkit/hkhealthstore/1614175-enablebackgrounddelivery#discussion): "Some data types, such as step counts, have a minimum frequency of HKUpdateFrequency.hourly. This frequency is enforced transparently".
So yes, it does depend on the data type for the frequency and for some data types, even if you include frequency as immediate. That being said, I have been having some problems triggering HKObserverQuery for heart rate so I am not entirely sure if the frequency is reliable (it is possible there is a timeout for observer tasks).