Search code examples
ioswatchkitapple-watchwatchhealthkit

How to get Heart Rate Data near by Real Time from Health Kit in iOS?


I have created a session in Watch and updating Heart Rate Data in Health Kit. Now, I want to Display the current Heart Rate in my iPhone Screen. Watch sensor update the Heart Rate Data in Health kit BUT iPhone Application in NOT able to Fetch Real-Time Data from the Health kit. I have tested out below TWO scenarios. I have also recalled this method/function using timer BUT it is not getting real-time data.

Note: When I open Health App and Re-open my application then It will automatically refresh the data. If my application continuously in foreground then below code not refreshing latest data from the health kit

1. Tried to get Real Time Heart Rate Data using HKSampleQuery

        let calendar = NSCalendar.current
        let components = calendar.dateComponents([.year, .month, .day], from: Date())
        let startDate : NSDate = calendar.date(from: components)! as NSDate
        let endDate : Date = calendar.date(byAdding: Calendar.Component.day, value: 1, to: startDate as Date)!

        let predicate = HKQuery.predicateForSamples(withStart: startDate as Date, end: endDate, options:[])

        //descriptor
        let sortDescriptors = [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)]

        self.heartRateQuery = HKSampleQuery(sampleType: self.heartRateType, predicate: predicate, limit: 1, sortDescriptors: sortDescriptors, resultsHandler: { (query:HKSampleQuery,  results:[HKSample]?, error:Error?) in

            guard error == nil else { print("error in getting data"); return }

            self.collectCurrentHeartRateSample(currentSampleTyple: results)

        })

        self.healthStore.execute(self.heartRateQuery!)

2. Tried to get Real Time Heart Rate Data using HKAnchoredObjectQuery

    let sampleType : HKSampleType =  HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
    let predicate : NSPredicate =  HKQuery.predicateForSamples(withStart: startDate as Date, end: endDate, options: [])
    let anchor: HKQueryAnchor = HKQueryAnchor(fromValue: 0)

    let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: predicate, anchor: anchor, limit: HKObjectQueryNoLimit) { (query, samples, deletedObjects, anchor, error ) in

        self.collectCurrentHeartRateSample(currentSampleTyple: samples!, deleted: deletedObjects!)

    }

    anchoredQuery.updateHandler = { (query, samples, deletedObjects, anchor, error) -> Void in
        self.collectCurrentHeartRateSample(currentSampleTyple: samples!, deleted: deletedObjects!)
    }

    self.healthStore.execute(anchoredQuery)

=============================================

Parsed the Data

func collectCurrentHeartRateSample(currentSampleTyple : [HKSample]?, deleted : 

    [HKDeletedObject]?){

    //    func collectCurrentHeartRateSample(currentSampleTyple : [HKSample]?){

            DispatchQueue.main.async {

                self.currentHeartRateSample = currentSampleTyple

                //Get Last Sample of Heart Rate
                self.currentHeartLastSample = self.currentHeartRateSample?.last
                print("lastSample : \(String(describing: self.currentHeartLastSample))")

                if self.currentHeartLastSample != nil {

                    let result = self.currentHeartLastSample as! HKQuantitySample

                    let heartRateBPM = result.quantity.doubleValue(for: HKUnit(from: "count/min"))
                    let heartRateBPMUnit = "count/min"

                    let deviceUUID = self.currentHeartLastSample?.uuid
                    let deviceIdentity = result.sourceRevision.source.name
                    let deviceProductName = self.currentHeartLastSample?.device?.name
                    let deviceProductType = result.sourceRevision.productType
                    let deviceOSVersion = result.sourceRevision.version

                    let startDate = self.currentHeartLastSample?.startDate
                    let endDate = self.currentHeartLastSample?.endDate

                    self.aCollectionView.reloadData()
                }


            }

        }

Solution

  • Here is my own analysis regarding get nearby real-time Heart Rate.

    1. If you are accessing Health Kit data using iPhone app, In this scenario, Health Kit DB NOT frequently updated/refreshed. So, your app not able to get real-time latest updated data through iPhone app.

    2. Using a watch app, you can access near real-time data through Health Kit DB. Watch app is able to get the real-time latest updated Health Kit Data.

    3. You need to transfer data from watch to iPhone app. Here is a code for your reference. You can write code as per your requirement. You just need to access Heart Rate through HKQuery

    let defaultSession = WCSession.default
    
    let healthStore = HKHealthStore()
    
    var currentHeartRateSample : [HKSample]?
    
    var currentHeartLastSample : HKSample?
    
    var currentHeartRateBPM = Double()
    
    
    //Get Heart Rate from Health Kit
    
    func getCurrentHeartRateData(){
    
        let calendar = Calendar.current
        let components = calendar.dateComponents([.year, .month, .day], from: Date())
        let startDate : Date = calendar.date(from: components)!
        let endDate : Date = calendar.date(byAdding: Calendar.Component.day, value: 1, to: startDate as Date)!
    
        let sampleType : HKSampleType =  HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
        let predicate : NSPredicate =  HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: [])
        let anchor: HKQueryAnchor = HKQueryAnchor(fromValue: 0)
    
        let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: predicate, anchor: anchor, limit: HKObjectQueryNoLimit) { (query, samples, deletedObjects, anchor, error ) in
    
            if samples != nil {
    
                self.collectCurrentHeartRateSample(currentSampleTyple: samples!, deleted: deletedObjects!)
    
            }
    
        }
    
        anchoredQuery.updateHandler = { (query, samples, deletedObjects, anchor, error) -> Void in
            self.collectCurrentHeartRateSample(currentSampleTyple: samples!, deleted: deletedObjects!)
        }
    
        self.healthStore.execute(anchoredQuery)
    
    }
    
    
    //Retrived necessary parameter from HK Sample
    func collectCurrentHeartRateSample(currentSampleTyple : [HKSample]?, deleted : [HKDeletedObject]?){
    
            self.currentHeartRateSample = currentSampleTyple
    
            //Get Last Sample of Heart Rate
            self.currentHeartLastSample = self.currentHeartRateSample?.last
    
            if self.currentHeartLastSample != nil {
    
                let lastHeartRateSample = self.currentHeartLastSample as! HKQuantitySample
    
                self.currentHeartRateBPM = lastHeartRateSample.quantity.doubleValue(for: HKUnit(from: "count/min"))
                let heartRateStartDate = lastHeartRateSample.startDate
                let heartRateEndDate = lastHeartRateSample.endDate
    
                //Send Heart Rate Data Using Send Messge
    
                DispatchQueue.main.async {
    
                    let message = [
                        "HeartRateBPM" : "\(self.currentHeartRateBPM)",
                        "HeartRateStartDate" : "\(heartRateStartDate)",
                        "HeartRateEndDate" : "\(heartRateEndDate)"
                    ]
    
    //Transfer data from watch to iPhone
                    self.defaultSession.sendMessage(message, replyHandler:nil, errorHandler: { (error) in
                        print("Error in send message : \(error)")
                    })
    
                }
    
            }
    
    
    }