HealthKit HKObserverQuery not firing consistently

1.1k Views Asked by At

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?

1

There are 1 best solutions below

0
On

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