Seeking Advice on Real-Time Heartbeat Data from Apple Watch: Heart Rate vs. HKElectrocardiogram

Hello,

I am developing an Apple Watch app in Swift and SwiftUI that needs to receive real-time heartbeat data to visualize the time intervals between heartbeats. The app draws circles on the screen where the size and position of each circle are based on the time interval between consecutive heartbeats.

I am currently using the HKQuantityType for .heartRate from HealthKit to get heart rate data and calculate the intervals. However, I am wondering if this is the best approach for my requirement. I came across the HKElectrocardiogram class, and I am not sure if it would be a better fit for obtaining real-time heartbeat intervals.

My questions are:

  1. Real-Time Heartbeats:

    • Is HKQuantityType for .heartRate the most appropriate way to get real-time heartbeat data for calculating intervals between beats?
    • Can HKElectrocardiogram provide real-time heartbeat intervals, or is it more suited for detailed ECG recordings rather than instantaneous heartbeats?
  2. Accuracy and Performance:

    • Which method provides the most accurate and real-time data for heartbeat intervals?
    • Are there any other APIs or services in the Apple Watch ecosystem that I should consider for this purpose?
  3. Best Practices:

    • What are the best practices for implementing real-time heartbeat monitoring in an Apple Watch app?
    • Are there any sample projects or documentation that could help me understand the optimal way to achieve this?

Here is a brief overview of my current implementation using HKQuantityType for .heartRate:

import Foundation
import HealthKit

class HeartRateMonitor: NSObject, ObservableObject {
    @Published var heartRate: Double = 0.0
    @Published var intervals: [TimeInterval] = []
    private var lastHeartRateTimestamp: Date?
    private var healthStore: HKHealthStore?
    private let heartRateQuantityType = HKObjectType.quantityType(forIdentifier: .heartRate)
    private let appStartTime: Date
    
    override init() {
        self.appStartTime = Date()
        super.init()
        
        if HKHealthStore.isHealthDataAvailable() {
            self.healthStore = HKHealthStore()
            self.requestAuthorization()
        }
    }
    
    private func requestAuthorization() {
        guard let heartRateQuantityType = self.heartRateQuantityType else { return }
        
        healthStore?.requestAuthorization(toShare: nil, read: [heartRateQuantityType]) { success, error in
            if success {
                self.startMonitoring()
            }
        }
    }
    
    func startMonitoring() {
        guard let heartRateQuantityType = self.heartRateQuantityType else { return }
        
        let query = HKAnchoredObjectQuery(
            type: heartRateQuantityType,
            predicate: nil,
            anchor: nil,
            limit: HKObjectQueryNoLimit) { (query, samples, deletedObjects, newAnchor, error) in
                guard let samples = samples as? [HKQuantitySample] else { return }
                self.process(samples: samples)
            }
        
        query.updateHandler = { (query, samples, deletedObjects, newAnchor, error) in
            guard let samples = samples as? [HKQuantitySample] else { return }
            self.process(samples: samples)
        }
        
        healthStore?.execute(query)
    }
    
    private func process(samples: [HKQuantitySample]) {
        for sample in samples {
            if sample.endDate > appStartTime {
                let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
                let heartRate = sample.quantity.doubleValue(for: heartRateUnit)
                
                DispatchQueue.main.async {
                    self.heartRate = heartRate
                    
                    if let lastTimestamp = self.lastHeartRateTimestamp {
                        let interval = sample.endDate.timeIntervalSince(lastTimestamp)
                        self.intervals.append(interval)
                    }
                    
                    self.lastHeartRateTimestamp = sample.endDate
                }
            }
        }
    }
}

Thank you for your guidance and suggestions!

Answered by DTS Engineer in 789474022

First, I probably don't call the HealthKit data "real-time". On the Apple Watch side, the data is only available for apps after it has been gathered from the sensors, processed, and saved to the HealthKit store. On the paired iPhone side, the data is only available after the HealthKit store is synchronized, which can take a while.

If you focus on heart rates, using HKAnchoredObjectQuery to query the HKQuantityType(.heartRate) samples is the right way to go, and so you are on the right track. HKAnchoredObjectQuery triggers its updateHandler when a new sample (after the specified anchor) becomes available, which is better than executing multiple sample queries (HKSampleQuery). The same logic applies to other samples like HKElectrocardiogram.

A heart rate sample doesn't have the heart beats though. If you'd look into heart beats, consider querying the heartbeat series samples (HKSeriesType.heartbeat()). For more information, see HKHeartbeatSeriesSample and HKHeartbeatSeriesQuery.

In that matters, HealthKit also provides Heart rate variability (HRV) samples. See heartRateVariabilitySDNN.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

First, I probably don't call the HealthKit data "real-time". On the Apple Watch side, the data is only available for apps after it has been gathered from the sensors, processed, and saved to the HealthKit store. On the paired iPhone side, the data is only available after the HealthKit store is synchronized, which can take a while.

If you focus on heart rates, using HKAnchoredObjectQuery to query the HKQuantityType(.heartRate) samples is the right way to go, and so you are on the right track. HKAnchoredObjectQuery triggers its updateHandler when a new sample (after the specified anchor) becomes available, which is better than executing multiple sample queries (HKSampleQuery). The same logic applies to other samples like HKElectrocardiogram.

A heart rate sample doesn't have the heart beats though. If you'd look into heart beats, consider querying the heartbeat series samples (HKSeriesType.heartbeat()). For more information, see HKHeartbeatSeriesSample and HKHeartbeatSeriesQuery.

In that matters, HealthKit also provides Heart rate variability (HRV) samples. See heartRateVariabilitySDNN.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Seeking Advice on Real-Time Heartbeat Data from Apple Watch: Heart Rate vs. HKElectrocardiogram
 
 
Q