Search code examples
watchkitheartbeat

How to get heart rate in watchOS 3+ without using a workout session


I would like to collect the heart rate of the user programmatically by a watchkit extension, but not during a workout session.

As far as I know watchOS 3+ doesn't collect heart rate if the user is moving: however as soon as he's resting for a while (10 minutes ?) I'd like to get somehow the current bpm value by the HealthKit API.


Solution

  • Since you are having a companion iPhone app, you may query heart rate updates from there.

    • You just need to have an iPhone with paired Apple watch (which is obvious)
    • The Default Apple Watch Heart Rate monitor app updates the HealthKit data immediately only when it is in the foreground.
    • When the Default Apple Watch Heart Rate monitor app is in the Background, it updates the HealthKit data at the interval of 9-10 mins. This code should do the work:

      import UIKit
      import HealthKit
      
      class ViewController: UIViewController {
      @IBOutlet weak var heartRateLabel: UILabel!
      @IBOutlet weak var timeStampLabel: UILabel!
      
      var hkStore: HKHealthStore?
      let heartRateUnit: HKUnit = HKUnit.count().unitDivided(by: .minute())
      var healthStore: HKHealthStore?
      override func viewDidLoad() {
          super.viewDidLoad()
          healthStore = HKHealthStore()
          let sampleTypes = HKSampleType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
      
          healthStore?.requestAuthorization(toShare: [sampleTypes], read: [sampleTypes], completion: { (success, error) in
              if (error != nil) {
                  print(error!.localizedDescription)
              }
          })
      
          getSamples()
      }
      func getSamples() {
          let heartrate = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)
          let sort = [
              NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
          ]
          let sampleQuery = HKSampleQuery(sampleType: heartrate!, predicate: nil, limit: 1, sortDescriptors: sort, resultsHandler: { [unowned self] (query, results, error) in
              if let results = results as? [HKQuantitySample]
              {
                  let sample = results[0] as HKQuantitySample
                  let value = sample.quantity.doubleValue(for: self.heartRateUnit)
                  let rate = results[0]
                  print(value, rate)
                  self.updateHeartRate(samples: results)
              }
          })
          healthStore?.execute(sampleQuery)
      }
      
      func updateHeartRate(samples: [HKSample]?) {
          guard let heartRateSamples = samples as? [HKQuantitySample] else {return}
          DispatchQueue.main.async {
              guard let sample = heartRateSamples.first else{return}
              let value = sample.quantity.doubleValue(for: self.heartRateUnit)
              self.heartRateLabel.text = String(UInt16(value))
              let date = sample.startDate
              let dateFormatter = DateFormatter()
              dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
              self.timeStampLabel.text = dateFormatter.string(from: date)
           }
       }
      }
      

    Notice that the query need to be fire periodically.