-->

What's the logic in HKObserverQuery background

2019-07-13 02:44发布

问题:

I need my app to sync between HealthKit and our database while it's in the background. I just can't wrap my head around the logic that determines how and when the HKObserverQueries run their updateHandlers. I need data of various different sample types, so I assume I need an observer query for each one. Right?

According to Apple about function enableBackgroundDeliveryForType, "HealthKit wakes your app whenever new samples of the specified type are saved to the store." But if I enable background deliveries and execute observerqueries for, say, blood glucose and weight, they both seem to run their updatehandlers whenever I input data in either one of them in the Health app. This also seems to happen even when I enable background delivery for only one of the sample types. Why?

func startObserving(completion: ((success: Bool) -> Void)!) {

    let sampleTypeBloodGlucose = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)!
    let sampleTypeWeight = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!

    // Enable background delivery for blood glucose
    self.healthKitStore.enableBackgroundDeliveryForType(sampleTypeBloodGlucose, frequency: .Immediate) {
        (success, error) in

        if error != nil {
            abort()
        }
    }

    // Enable background delivery for weight
    self.healthKitStore.enableBackgroundDeliveryForType(sampleTypeWeight, frequency: .Immediate) {
        (success, error) in

        if error != nil {
            abort()
        }
    }

    // Define update handlers for background deliveries
    let updateHandlerBloodGlucose: (HKObserverQuery, HKObserverQueryCompletionHandler, NSError?) -> Void = {
        query, completionHandler, error in

        if error != nil {
            abort()
        }

        // Handle data and call the completionHandler
        completionHandler()
    }

    let updateHandlerWeight: (HKObserverQuery, HKObserverQueryCompletionHandler, NSError?) -> Void = {
        query, completionHandler, error in

        if error != nil {
            abort()
        }

        // Handle data and call the completionHandler
        completionHandler()
    }

    let observerQueryBloodGlucose = HKObserverQuery(sampleType: sampleTypeBloodGlucose, predicate: nil, updateHandler: updateHandlerBloodGlucose)
    healthKitStore.executeQuery(observerQueryBloodGlucose)

    let observerQueryWeight = HKObserverQuery(sampleType: sampleTypeWeight, predicate: nil, updateHandler: updateHandlerWeight)
    healthKitStore.executeQuery(observerQueryWeight)

    completion(success: true)

}

回答1:

If you are using the background delivery feature of HealthKit, then yes, you do need to open an HKObserverQuery for each type of data you observe and handle the invocations of the updateHandler and call the provided completion when you are finished. However, the updateHandler of HKObserverQuery is advisory and invocations do not necessarily correspond one-to-one with changes to the HealthKit database (there is not always enough information available to determine what your app has processed and what it hasn't, so sometimes the handler may run when there isn't new data).

Don't worry about understanding or controlling precisely when updateHandler runs - just use it as a trigger to perform other queries that will actually give you up-to-date values from HealthKit. If you need to know precisely which samples in HealthKit are new, for example, then your app should use an HKAnchoredObjectQuery.