简体   繁体   中英

HealthKit Observer not working while app is in background mode

I have followed Apple Docs and Several threads on stackoverflow on how to achieve background fetching of data from Health Store. So far I have:

  • Added HealthKit Entitlement to my appID
  • Added Required Background Modes
  • Added the code to AppDelegate.swift as Apple suggest ( the snippet below is not following OOP just for facility to state this way here )

This is my code (swift): If your answer is in Obj-C and works, please state it as well, I will have to translate it, but that's no problem.

AppDelegate.swift

var healthStore: HKHealthStore?
var bpmSamples: [HKQuantitySample]?

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    let dataTypesToWrite = [ ]
    let dataTypesToRead = [
        HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate),
        HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex),
        HKCharacteristicType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)
    ]
    if self.healthStore == nil {
        self.healthStore = HKHealthStore()
    }
    self.healthStore?.requestAuthorizationToShareTypes(NSSet(array: dataTypesToWrite as [AnyObject]) as Set<NSObject>,
        readTypes: NSSet(array: dataTypesToRead) as Set<NSObject>, completion: {
            (success, error) in
            if success {
                self.addQueryObserver()
                println("User completed authorisation request.")
            } else {
                println("The user cancelled the authorisation request. \(error)")
            }
    })
    return true
}

func addQueryObserver(){
    let sampleType =
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)

    let query = HKObserverQuery(sampleType: sampleType, predicate: nil) {
        query, completionHandler, error in
        if error != nil {
            // Perform Proper Error Handling Here...
            println("*** An error occured while setting up the stepCount observer. \(error.localizedDescription) ***")
            abort()
        }
        println("query is running")

        self.performQueryForHeartBeatSamples()
        completionHandler()
    }
    healthStore?.executeQuery(query)
    healthStore?.enableBackgroundDeliveryForType(sampleType, frequency:.Immediate, withCompletion:{
        (success:Bool, error:NSError!) -> Void in
        let authorized = self.healthStore!.authorizationStatusForType(sampleType)
        println("HEALTH callback success", success)
        println("HEALTH callback authorized", sampleType)
    })
    if HKHealthStore.isHealthDataAvailable() == false {
        println("HEALTH data not available")
        return
    } else {
        println("HEALTH OK")
        self.performQueryForHeartBeatSamples()
    }
}
 // MARK: - HealthStore utility methods
func performQueryForHeartBeatSamples() {
    let endDate = NSDate()
    let startDate = NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMonth, value: -2, toDate: endDate, options: nil)

    var heartRate : HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)

    let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: .None)
    let query = HKSampleQuery(sampleType: heartRate, predicate: predicate, limit: 0, sortDescriptors: nil, resultsHandler: {
        (query, results, error) in
        if results == nil {
            println("There was an error running the query: \(error)")
        }
        dispatch_async(dispatch_get_main_queue()) {
            self.bpmSamples = results as? [HKQuantitySample]
            let heartRateUnit: HKUnit = HKUnit.countUnit().unitDividedByUnit(HKUnit.minuteUnit())
            if self.bpmSamples?.count > 0 {
                if let sample = self.bpmSamples?[self.bpmSamples!.count - 1] {
                    println(sample.quantity!.description)
                    let quantity = sample.quantity
                    var value = quantity.doubleValueForUnit(heartRateUnit)
                    println("bpm: \(value)")
                }
            }
            else {
                println("No Data")
            }
        }
    })
    self.healthStore?.executeQuery(query)
}

So, the problem is that I only receive updates when I resume my app from background to active state manually.. HKObserverQuery doesn't seems to be working for me while on background mode.

Any suggestions?

In my experiences, frequency ".Immediate" doesn't work well. The handler of queries are inserted into background queue. If the matching samples are frequently added or iOS is busy, the immediate frequency doesn't work well.

In addition, you cannot use HKSampleQuery in HKObserverQuery . updateHandler of HKObserverQuery may work, but resultHandler of HKSampleQuery will not. The handler of an observer query can be executed in the background mode but the one of a sample query cannot be executed in the background mode.

You should know that ONLY HKObserverQuery can be used in the background

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM