Queried heart rate samples in the past interval have lower resolution than recent samples

I am querying heart rate samples taken during a workout. For all of the mentioned cases below I'm wearing an Apple Watch and using the stock Workouts.app in the watch.



If I query heart rate samples for a recent workout, it being yesterday or the past month for example, I get the full samples. If I query a workout let's say, two/three months in the past the samples I get back look "choppy" with a lot of missing data. (See attached image)



imgur.com/a/FbMSBoa


How am I getting the heart rate samples?

Code Block swift
extension HKHealthStore: HKHealthStoreCombine {
    public func get<T>(sample: T, start: Date, end: Date, limit: Int = HKObjectQueryNoLimit) -> AnyPublisher<[HKQuantitySample], Error> where T: HKObjectType {
        let subject = PassthroughSubject<[HKQuantitySample], Error>()        
        let sampleType = HKSampleType.quantityType(forIdentifier: HKQuantityTypeIdentifier(rawValue: sample.identifier))!
        let predicate = HKQuery.predicateForSamples(withStart: start, end: end)
        
        let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: limit, sortDescriptors: nil, resultsHandler: { (query, samples, error) in
            guard error == nil else {
                logger.error("Error fetching samples of type \(sample.description) from \(start) to \(end) with a limit of \(limit): \(error!.localizedDescription)")
                subject.send(completion: .failure(error!))
                return
            }
            let samples = samples as? [HKQuantitySample] ?? []
            logger.log("Successfully fetched \(samples.count) samples of type \(sample.description) from \(start) to \(end) with a limit of \(limit)")
            subject.send(samples)
            subject.send(completion: .finished)
        })
        self.execute(query)
        return subject.eraseToAnyPublisher()
    }
}


Code Block swift
healthStore.get(sample: HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!, start: workout.startDate, end: workout.endDate)
            .map({
                $0.compactMap({ $0.quantity.doubleValue(for: UserUnits.shared().heartCountUnits) })
            })
            .replaceError(with: [])
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: {
                self.heartRate = $0
            })
            .store(in: &bag)




Is this some kind of wrong way that I am working with HealthKit? I don't know what the issue can be but it looks like accessing heart samples in the "past" are somewhat archived and I don't get the full resolution.


Replies

I have noticed the same issue but cannot find documentation on this. I have previously exported Workout Data using RunGap and these files show the full resolution of the heart rate data, but the very same workouts on my iPhone older than 3 months now have only abbreviated heart rate data available when you do a query. At some point the iPhone appears to have cleared-out the HKSamples associated with workouts older than a certain time, and which were available in the past when I did those rungap exports which is very frustrating. If anyone knows how to restore these missing data I would be grateful to know how.

What you're getting back are HKDiscreteQuantitySample for heart rate. You can query for the higher resolution heart rate by using HKQuantitySeriesSampleQuery. You should also be seeing the count property on HKQuantitySample to be greater than 1. See this WWDC session for info on how to use this.

Older workout data (calories, distance, heart rate) gets rewritten as series which is what you're seeing.

  • I've noticed this issue for several years now and never found any information or documentation on what's going on. Thank you very much for clarifying this. Any further information on what defines "older workout data", as in how old, specifically interested in heart rate. Tine to get on adopting HKQuantitySeriesSampleQuery!

Add a Comment