Monitor heart rate from HealthKit --> HKAnchoredObjectQuery only called after applicationDidBecomeActive (BUG or FEATURE?)
Asked Answered
B

2

6

I am writing a simple app to monitor the heart rate (HKQuantityTypeIdentifierHeartRate) from HealthKit whenever a new health rate value is written to HealthKit.

As seen at WWDC2015 (session 203) I am using a HKAnchoredObjectQuery which should work for adding and deleting objects. Whenever I start the app I am calling the HKQuery for the newest objects and executingQuery which works fine!!! But I am getting no new samples even if the samples are there, but if I bring the app to the background and again to the foreground I am getting all the new heart rates. IS IT A BUG? Or what shall I do to monitor the heart rate without bringing the app to the back- and foreground?

Here is the code I am using (everything is stored in the AppDelegate), I am calling [self requestAccessDataTypes]; from didFinishLaunchingWithOptions:

[healthStore enableBackgroundDeliveryForType:sampleType frequency:HKUpdateFrequencyImmediate withCompletion:^(BOOL success, NSError *error) {}];

HKQuery *query = [self createHeartRateStreamingQuery:datum];
    if (query) {
        [healthStore executeQuery:query];
    }
    else
    {
        NSLog(@"workout can not start");
    }

-(HKQuery*)createHeartRateStreamingQuery:(NSDate*)workoutStartDate
{
    NSLog(@"%@ - createHeartRateStreamingQuery", [self class]);

    if ([HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate]) {
        HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];

        HKAnchoredObjectQuery * heartRateQuery = [[HKAnchoredObjectQuery alloc] initWithType:quantityType predicate:nil anchor:anchor limit:HKObjectQueryNoLimit resultsHandler:^(HKAnchoredObjectQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable sampleObjects, NSArray<HKDeletedObject *> * _Nullable deletedObjects, HKQueryAnchor * _Nullable newAnchor, NSError * _Nullable error) {

            if (!error) {
                anchor = newAnchor;
                [self updateHeartRate:sampleObjects];

            }

        }];
        heartRateQuery.updateHandler = ^void(HKAnchoredObjectQuery *query, NSArray<__kindof HKSample *> * __nullable addedObjects, NSArray<HKDeletedObject *> * __nullable deletedObjects, HKQueryAnchor * __nullable newAnchor, NSError * __nullable error)
        {
            if (!error) {
                anchor = newAnchor;
                [self updateHeartRate:addedObjects];

            }

        };
        return heartRateQuery;
    }
    return nil;
}
Beside answered 28/10, 2015 at 22:42 Comment(0)
T
1

You are missing a crucial piece for observing changes in HealthKit. It's called HKObserverQuery.

Docs

Observer queries set up a long-running task on a background queue. This task watches the HealthKit store, and alerts you whenever matching data is saved to or removed from the store. Observer queries let your app respond to changes made by other apps and devices.

Recap:

You have to wrap your HKAnchoredObjectQuery in HKObserverQuery with background delivery enabled in order to get notified about updates. You can then execute your query whenever that happens.

Note 1: HKObserverQuery's update handler will NOT give you any Apple Health data samples. You still have to execute your HKAnchoredObjectQuery with a proper anchor to get the samples.

Note 2: You have to set up HKObserverQuery every time your app launches.

For further info, look at my answer here.

Toile answered 5/2, 2016 at 6:30 Comment(0)
F
1

Right now (iOS 9.1, WatchOS 2.0.1), it is not possible to get the latest data from HealthKit through an iOS app. It was possible in the WWDC demo because the code was running on the WatchOS app's ExtensionDelegate rather than on the iOS app. There is a rdar bug report filed here.

To get the latest data on iOS, it's not possible without creating a WatchOS app. With a WatchOS app you could use a Workout Session and Watch Connectivity to send heart rate data to the iOS app every time it changes.

Of course, this doesn't help if your heart rate data isn't coming from an Apple Watch. Hopefully it'll be fixed in an upcoming release.

Floreneflorentia answered 2/12, 2015 at 11:2 Comment(2)
Thank you for you answer but I am using the automatic transfer of the HealthKit data from the watch to the parent app, and as I wrote in my request the data is transferred more or less in realtime! Whenever I start the app I am calling the HKQuery for the newest objects and executingQuery ON THE PARENT APP (e.g. iPhone) which works fine!!! But I am getting no new samples even if the samples are there, but if I bring the app to the background and again to the foreground I am getting all the new heart rates.Beside
Surely it's not ideal to have to close your app to get the latest data? I was trying to allude to this problem, and hopefully Apple will make it possible to get the data as it enters HealthKit on iOS without workarounds like this.Floreneflorentia
T
1

You are missing a crucial piece for observing changes in HealthKit. It's called HKObserverQuery.

Docs

Observer queries set up a long-running task on a background queue. This task watches the HealthKit store, and alerts you whenever matching data is saved to or removed from the store. Observer queries let your app respond to changes made by other apps and devices.

Recap:

You have to wrap your HKAnchoredObjectQuery in HKObserverQuery with background delivery enabled in order to get notified about updates. You can then execute your query whenever that happens.

Note 1: HKObserverQuery's update handler will NOT give you any Apple Health data samples. You still have to execute your HKAnchoredObjectQuery with a proper anchor to get the samples.

Note 2: You have to set up HKObserverQuery every time your app launches.

For further info, look at my answer here.

Toile answered 5/2, 2016 at 6:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.