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

2

10

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()
                }


            }

        }
Spoonbill answered 14/12, 2018 at 13:36 Comment(0)
S
3

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)")
                })

            }

        }


}
Spoonbill answered 16/3, 2019 at 2:57 Comment(0)
T
3

I think the best method is to simply send the heart rate data to the phone app using Watch Communication.

In Watch code:

func send(heartRate: Int) {
    guard WCSession.default.isReachable else {
        print("Phone is not reachable")
        return
    }

    WCSession.default.sendMessage(["Heart Rate" : heartRate], replyHandler: nil) { error in
        print("Error sending message to phone: \(error.localizedDescription)")
    }
 }

and on phone you receive the data with:

func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
    if let heartRate = message["Heart Rate"] {
        print("Received heart rate: \(heartRate)")
    } else {
        print("Did not receive heart rate =[")
    }
}

This should happen in pretty much real time. Alternatively there is another less reliable solution (imo) which is to just perform the heart rate query once every 5 seconds or so, but if I understand correctly you've already tried that and it didn't work.

Tetralogy answered 18/12, 2018 at 0:56 Comment(0)
S
3

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)")
                })

            }

        }


}
Spoonbill answered 16/3, 2019 at 2:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.