Search code examples
swifthealthkithksamplequery

HealthKit fetch data between interval


I have a little problem grasping HealthKit. I want to get heart rate from HealthKit with specific time. I have done this in the past (until I noticed that I couldn't fetch data when the phone was locked)

 func retrieveMostRecentHeartRateSample(completionHandler: (sample: HKQuantitySample) -> Void) {
    let sampleType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
    let predicate = HKQuery.predicateForSamplesWithStartDate(NSDate.distantPast() as! NSDate, endDate: NSDate(), options: HKQueryOptions.None)
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)

    let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: 1, sortDescriptors: [sortDescriptor])
        { (query, results, error) in
            if error != nil {
                println("An error has occured with the following description: \(error.localizedDescription)")
            } else {
                let mostRecentSample = results[0] as! HKQuantitySample
                completionHandler(sample: mostRecentSample)
            }
        }
    healthKitStore.executeQuery(query)
}

var observeQuery: HKObserverQuery!

func startObservingForHeartRateSamples() {
    println("startObservingForHeartRateSamples")
    let sampleType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)

    if observeQuery != nil {
        healthKitStore.stopQuery(observeQuery)
    }

    observeQuery = HKObserverQuery(sampleType: sampleType, predicate: nil) {
        (query, completionHandler, error) in

        if error != nil {
            println("An error has occured with the following description: \(error.localizedDescription)")
        } else {
           self.retrieveMostRecentHeartRateSample {
                (sample) in
                dispatch_async(dispatch_get_main_queue()) {
                let result = sample
                let quantity = result.quantity
                let count = quantity.doubleValueForUnit(HKUnit(fromString: "count/min"))
                println("sample: \(count)")
                heartChartDelegate?.updateChartWith(count)
                }
            }
        }
    }
    healthKitStore.executeQuery(observeQuery)
}

This code will fetch the latest sample every time it is a change in HealthKit. But as I said earlier, it won't update when the phone is locked. I tried using:

self.healthKitStore.enableBackgroundDeliveryForType(HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate), frequency: HKUpdateFrequency.Immediate) { (success, error) in
if success{
   println("success")
} else {
   println("fail")
}            

}

But this didn't work and as I found out there was a bug that Apple said it wasn't working as they wanted. Guess it is some security-thing.

But then I thought, maybe I can request samples between a startTime and endTime. For example i have EndTime(2015-05-31 10:34:45 +0000) and StartTime(2015-05-31 10:34:35 +0000). So my question is how can I get heart rate samples between these two times.

I guess I must do it in the

HKQuery.predicateForSamplesWithStartDate(myStartTime, endDate: myEndTime, options: HKQueryOptions.None)

But when I tried it didn't find anything. Maybe I got this all wrong...

I am using a heart rate monitor on my chest and I know that I get some values in healthKit within the start and end time.

Edit:

Ok I tried it and it is working some times, not always. Someone has an idea?

func fetchHeartRates(endTime: NSDate, startTime: NSDate){
    let sampleType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
    let predicate = HKQuery.predicateForSamplesWithStartDate(startTime, endDate: endTime, options: HKQueryOptions.None)
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)

    let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: 100, sortDescriptors: [sortDescriptor])
        { (query, results, error) in
            if error != nil {
                println("An error has occured with the following description: \(error.localizedDescription)")
            } else {
                for r in results{
                    let result = r as! HKQuantitySample
                    let quantity = result.quantity
                    let count = quantity.doubleValueForUnit(HKUnit(fromString: "count/min"))
                    println("sample: \(count) : \(result)")
                }
            }
    }
    healthKitStore.executeQuery(query)
}

Edit 2:

It was working but I couldn't call it the way I did. So I fetched it a couple of seconds later and it worked fine :)


Solution

  • ...But as I said earlier, it won't update when the phone is locked...Guess it is some security-thing.

    You are correct.

    From HealthKit Framework Reference:

    Because the HealthKit store is encrypted, your app cannot read data from the store when the phone is locked. This means your app may not be able to access the store when it is launched in the background. However, apps can still write data to the store, even when the phone is locked. The store temporarily caches the data and saves it to the encrypted store as soon as the phone is unlocked.

    If you want your app to be alerted when there are new results, you should review Managing Background Delivery:

    enableBackgroundDeliveryForType:frequency:withCompletion:

    Call this method to register your app for background updates. HealthKit wakes your app whenever new samples of the specified type are saved to the store. Your app is called at most once per time period defined by the specified frequency.