Getting updated heart rate data from Health Kit - ios

I wrote this code to get heart rate I am using NSArray and getting average of heart rate.
Now the question is the Apple Watch is updating data in Health Kit and I want the updated average heart beats being refreshed at every 1 minutes.
I am stuck at this point, please help?
-(double)get_heartRates
{
//code to get the updated heart beats
NSDate *startDate1 = [NSDate distantPast];
NSPredicate *Predicate = [HKQuery predicateForSamplesWithStartDate:startDate1 endDate:[NSDate date] options:HKQueryOptionStrictEndDate];
HKSampleType *object = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
sum_Of_HeartRates=0.0;
HKAnchoredObjectQuery *heartQuery = [[HKAnchoredObjectQuery alloc] initWithType:object predicate:Predicate anchor:self.lastAnchor limit:0 resultsHandler:^(HKAnchoredObjectQuery *query, NSArray<HKSample *> *sampleObjects, NSArray<HKDeletedObject *> *deletedObjects, HKQueryAnchor *newAnchor, NSError *error) {
NSLog(#"Sample counts:%ld",sampleObjects.count);
for(int i=0;i<(int)sampleObjects.count;i++)
{
HKQuantitySample *sample = (HKQuantitySample *)[sampleObjects objectAtIndex:i];
HKQuantity *quantity = sample.quantity;
double bpm_Values= [quantity doubleValueForUnit:[HKUnit unitFromString:#"count/min"]];
sum_Of_HeartRates=sum_Of_HeartRates+bpm_Values;
}
avg_heartBeats=sum_Of_HeartRates/(int)sampleObjects.count;
}];
[heartQuery setUpdateHandler:^(HKAnchoredObjectQuery *query, NSArray<HKSample *> *SampleArray, NSArray<HKDeletedObject *> *deletedObjects, HKQueryAnchor *Anchor, NSError *error) {
HKQuantitySample *sample = (HKQuantitySample *)[SampleArray objectAtIndex:0];
HKQuantity *quantity = sample.quantity;
new_Updated_Data =[quantity doubleValueForUnit:[HKUnit unitFromString:#"count/min"]];
NSLog(#"new quantity:%f",new_Updated_Data);
}];
[self.healthStore executeQuery:heartQuery];
NSLog(#"updated data %f",new_Updated_Data);
return avg_heartBeats;
//todo:- to get background update fast and easy
}

Use HKObserverQuery to get a long running query that is updated in background:
https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKObserverQuery_Class/index.html
As long as no HKWorkoutSessionis running, your watch measures the heart rate every 10 minutes, so you don't get more values. When a HKWorkoutSession is running on your watch, you get values more frequently.
If you want to dive into HKWorkoutSession, Allan has a detailed tutorial: https://developer.apple.com/videos/play/wwdc2015/203/

Related

HKStatisticsCollectionQuery for fetching Heart Rate Health Kit

I have been trying for a while for fetching heart rate in order to plot in a graph. As mentioned in docs heart rate can be fetched by HKStatisticsCollectionQuery. I am trying to fetch a week's data from current date.
But I am unable to get the fetched data. Here is my code below for heart rate accessing using HKStatisticsCollectionQuery :
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *interval = [[NSDateComponents alloc] init];
NSDate *anchorDate = [[NSDate alloc] init];
NSDateComponents *anchorComponents =
[calendar components:NSCalendarUnitDay | NSCalendarUnitMonth |
NSCalendarUnitYear | NSCalendarUnitWeekday fromDate:[NSDate date]];
NSDate *currentDisplayEndDate = [NSDate date];
NSDate *newDate = [calendar startOfDayForDate: currentDisplayEndDate]; NSDate *startDate = [newDate dateByAddingTimeInterval:-6*24*60*60];
anchorDate = startDate;
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:self.startDate endDate:_currentDisplayEndDate options:HKQueryOptionStrictStartDate];
HKQuantityType *quantityType =
[HKObjectType quantityTypeForIdentifier:quantityId];
// Create the query
HKStatisticsCollectionQuery *query =
[[HKStatisticsCollectionQuery alloc]
initWithQuantityType:quantityType
quantitySamplePredicate:predicate
options:HKStatisticsOptionDiscreteMax
anchorDate:anchorDate
intervalComponents: interval];
// Set the results handler
query.initialResultsHandler =
^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) {
if (error) {
// Perform proper error handling here
NSLog(#"*** An error occurred while calculating the statistics: %# ***",
error.localizedDescription);
}
[results
enumerateStatisticsFromDate:startDate
toDate:endDate
withBlock:^(HKStatistics *result, BOOL *stop) {
HKQuantity *quantity = result.sumQuantity;
if (quantity) {
NSDate *date = result.startDate;
double value = [quantity doubleValueForUnit:[[HKUnit unitFromString:#"count/min"]];
// Call a custom method to plot each data point.
}
}];
};
[healthStore executeQuery:query];
My HKStatistics *results is returned as nil.Am I doing something wrong over here?
Problem in not where you thought, The results are returned with statistical query, but in case of heart rate, it does not give heart beat quantity along with that, so HKQuantity *quantity = result.sumQuantity; returns nil.
If you will check properly, you will see that results.statistics will give you some data about recorded heart rate, but no heart rate quantity, rather only, the start and end dates for the recorded data.
I would suggest, go ahead and you HKAnchoredQuery for the same, I will provide the code, here:
-(double)get_heartRates
{
//code to heart beats average, modify as needed
NSDate *startDate1 = [NSDate distantPast];
NSPredicate *Predicate = [HKQuery predicateForSamplesWithStartDate:startDate1 endDate:[NSDate date] options:HKQueryOptionStrictEndDate];
HKSampleType *object = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
sum_Of_HeartRates=0.0;
HKAnchoredObjectQuery *heartQuery = [[HKAnchoredObjectQuery alloc] initWithType:object predicate:Predicate anchor:self.lastAnchor limit:0 resultsHandler:^(HKAnchoredObjectQuery *query, NSArray<HKSample *> *sampleObjects, NSArray<HKDeletedObject *> *deletedObjects, HKQueryAnchor *newAnchor, NSError *error) {
NSLog(#"Sample counts:%ld",sampleObjects.count);
for(int i=0;i<(int)sampleObjects.count;i++)
{
HKQuantitySample *sample = (HKQuantitySample *)[sampleObjects objectAtIndex:i];
HKQuantity *quantity = sample.quantity;
double bpm_Values= [quantity doubleValueForUnit:[HKUnit unitFromString:#"count/min"]];
sum_Of_HeartRates=sum_Of_HeartRates+bpm_Values;
}
avg_heartBeats=sum_Of_HeartRates/(int)sampleObjects.count;
}];
[heartQuery setUpdateHandler:^(HKAnchoredObjectQuery *query, NSArray<HKSample *> *SampleArray, NSArray<HKDeletedObject *> *deletedObjects, HKQueryAnchor *Anchor, NSError *error) {
HKQuantitySample *sample = (HKQuantitySample *)[SampleArray objectAtIndex:0];
HKQuantity *quantity = sample.quantity;
new_Updated_Data =[quantity doubleValueForUnit:[HKUnit unitFromString:#"count/min"]];
NSLog(#"new quantity:%f",new_Updated_Data);
}];
[self.healthStore executeQuery:heartQuery];
NSLog(#"updated data %f",new_Updated_Data);
return avg_heartBeats;
}

HealthKit cannot read steps data

I'm working with HealthKit to read steps data from my iOS device.
here is my code:
if ([HKHealthStore isHealthDataAvailable]) {
__block double stepsCount = 0.0;
self.healthStore = [[HKHealthStore alloc] init];
NSSet *stepsType =[NSSet setWithObject:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]];
[self.healthStore requestAuthorizationToShareTypes:nil readTypes:stepsType completion:^(BOOL success, NSError * _Nullable error) {
if (success) {
HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:nil limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (error != nil) {
NSLog(#"results: %lu", (unsigned long)[results count]);
for (HKQuantitySample *result in results) {
stepsCount += [result.quantity doubleValueForUnit:[HKUnit countUnit]];
}
NSLog(#"Steps Count: %f", stepsCount);
} else {
NSLog(#"error:%#", error);
}];
[self.healthStore executeQuery:sampleQuery];
[self.healthStore stopQuery:sampleQuery];
NSLog(#"steps:%f",stepsCount);
}
}];
}
I build and run the code on an iPhone6 which does have steps data and in the Settings -> Privacy -> Health, the app does have been allowed to read data, but the log area only shows:
steps:0.000000
I put a break point on the for-loop and on the NSLog(#"error:%#", error), but the app does not break.
Anybody can help?
Try this code u just change the start date and End date.
-(void) getQuantityResult
{
NSInteger limit = 0;
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:currentDate endDate:[[NSDate date]dateByAddingTimeInterval:60*60*24*3] options:HKQueryOptionStrictStartDate];
NSString *endKey = HKSampleSortIdentifierEndDate;
NSSortDescriptor *endDate = [NSSortDescriptor sortDescriptorWithKey: endKey ascending: NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]
predicate: predicate
limit: limit
sortDescriptors: #[endDate]
resultsHandler:^(HKSampleQuery *query, NSArray* results, NSError *error){
dispatch_async(dispatch_get_main_queue(), ^{
// sends the data using HTTP
int dailyAVG = 0;
for(HKQuantitySample *samples in results)
{
dailyAVG += [[samples quantity] doubleValueForUnit:[HKUnit countUnit]];
}
lblPrint.text = [NSString stringWithFormat:#"%d",dailyAVG];
NSLog(#"%#",lblPrint.text);
NSLog(#"%#",#"Done");
});
}];
[self.healthStore executeQuery:query];
}
Your code stops the query immediately, before it has a chance to run. For this query, there is no reason to call stopQuery: at all unless you want to cancel a query before it finishes. Since the query is not long lived (it doesn't have an updateHandler), it will stop immediately after the resultsHandler is called.
The second problem is that your code attempts to log step count too soon. The query runs asynchronously, and the resultsHandler will be called on a background thread once the query completes. I'd suggest logging stepsCount inside the block.
Finally, if you want to count the user's steps you should us an HKStatisticsQuery instead of summing the results of an HKSampleQuery. HKStatisticsQuery is more efficient and will yield correct results when there are multiple sources of overlapping data in HealthKit. Your current implementation will double count the user's steps if they have both an iPhone and an Apple Watch, for instance.

HKAnchoredObjectQuery results handler doesn't fire after initial results

Our fitness app was using three anchored object queries to stream heart rate, active calories and distance; it was working just fine until a few weeks ago when we started optimizing the rest of the app's performance to be more responsive and query samples for past workouts. We believe we've restored the queries back to the way they were before, and we have very similar code but written in Swift for a separate fitness app working fine, but these HKAnchoredObjectQuerys just don't return further results after the initial results are handled (yes, a workout session is active). We're a bit stumped. Can anyone take a look and suggest where we might have gone wrong?
Thank you!
Here's an example of the heart rate query:
- (HKQuery*) createHeartRateStreamingQuery: (NSDate *) workoutStartDate {
NSPredicate * predicate = [HKQuery predicateForSamplesWithStartDate:workoutStartDate endDate:nil options:HKQueryOptionNone];
HKQueryAnchor *anchor = HKAnchoredObjectQueryNoAnchor;
if (_HRAnchor) {
anchor = _HRAnchor;
}
HKQuantityType * quantityType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
HKAnchoredObjectQuery *query =
[[HKAnchoredObjectQuery alloc]
initWithType:quantityType
predicate:predicate
anchor:anchor
limit:HKObjectQueryNoLimit
resultsHandler:^(HKAnchoredObjectQuery * query,
NSArray<HKSample *> * sampleObjects,
NSArray<HKDeletedObject *> * deletedObjects,
HKQueryAnchor * newAnchor,
NSError * error) {
if (error) {
NSLog(#"*** %s An error occured while performing the heartrate anchored object query. %# ***",
__PRETTY_FUNCTION__, error.localizedDescription);
abort();
} else {
if (newAnchor) {
_HRAnchor = newAnchor;
NSLog(#"*** %s: %i samples returned for startDate %# ***", __PRETTY_FUNCTION__, sampleObjects.count, workoutStartDate);
for (HKQuantitySample *sample in sampleObjects) {
[self updateHeartRate:sample];
}
for (HKDeletedObject *sample in deletedObjects) {
// [self removeHRSamples: sample]; //not using this for now
}
}
}
}];
return query;
}
- (void) updateHeartRate: (HKQuantitySample *) HRSample {
NSLog(#"%s: %#", __PRETTY_FUNCTION__, HRSample);
[_workoutHRSamplesArray addObject: HRSample];
}
In order to stream samples using an HKAnchoredObjectQuery, you must set the updateHandler property on the query. The results handler you provided to the initializer is only intended to run once for the existing samples that match your query.

iOS Healthkit reading workouts

Hi any one know how to fetch workouts data from HealthKit. i have seen in this tutorial http://www.raywenderlich.com/89733/healthkit-tutorial-with-swift-workouts it is in swift. i have tried in objective c based on that tutorial but getting results zero. there are questions for saving workouts but i want to read workouts data and display.
HKWorkoutType *workouttype = [HKWorkoutType workoutType];
HKWorkout *workout;
NSDate *startDate, *endDate;
NSDate *date1 = [NSDate date];
int daysTominus = -2;
startDate = [date1 dateByAddingTimeInterval:60*60*24*daysTominus];
int daysToAdd = 1;
NSDate *newDate1 = [date1 dateByAddingTimeInterval:60*60*24*daysToAdd];
endDate = newDate1;
workout = [HKWorkout workoutWithActivityType:HKWorkoutActivityTypeSwimming startDate:startDate endDate:endDate];
NSPredicate *predicate = [HKQuery predicateForObjectsFromWorkout:workout];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:workouttype
predicate:predicate
limit:HKObjectQueryNoLimit
sortDescriptors:#[sortDescriptor]
resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error)
{
if(!error && results){
for(HKQuantitySample *samples in results)
{
// your code here
NSLog(#"%#",samples);
}
}
}];
// Execute the query
[healthStore executeQuery:sampleQuery];
The problem seems to be your predicate.
This code works for me, I used running because I don't have swimming data but you can change it back to swimming:
-(void)retrieveWorkouts{
// 1. Predicate to read only running workouts
NSPredicate *predicate = [HKQuery predicateForWorkoutsWithWorkoutActivityType:HKWorkoutActivityTypeWalking];
// 2. Order the workouts by date
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:HKSampleSortIdentifierStartDate ascending:false];
// 3. Create the query
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:[HKWorkoutType workoutType]
predicate:predicate
limit:HKObjectQueryNoLimit
sortDescriptors:#[sortDescriptor]
resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error)
{
if(!error && results){
NSLog(#"Retrieved the following workouts");
for(HKQuantitySample *samples in results)
{
// your code here
HKWorkout *workout = (HKWorkout *)samples;
NSLog(#"%f",workout);
}
}else{
NSLog(#"Error retrieving workouts %#",error);
}
}];
// Execute the query
[healthStore executeQuery:sampleQuery];
}

Heart Rate data monthly

I am retrieving heart rate from Health Kit in my app. however i get the latest data from Health Kit, how can i get the data monthly wise? I mean last reading from each month.. i saw NSPredicate can be used to do so but i am not getting exactly how to do it..!! I am new to it, please help.!!
here is my code to access heart rate:
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:quantityType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:#[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!results) {
if (completion)
{
completion(nil, error);
}
return;
}
if (completion) {
// If quantity isn't in the database, return nil in the completion block.
HKQuantitySample *quantitySample = results.firstObject;
HKQuantity *quantity = quantitySample.quantity;
completion(quantity, error);
}
}];
[self executeQuery:query];
Code for predicate:
NSDate *startDate = [NSDate dateWithYear:[NSNumber numberWithInt:2015] month:[NSNumber numberWithInt:1] day:[NSNumber numberWithInt:1] hour:[NSNumber numberWithInt:00] minute:[NSNumber numberWithInt:00] seconds:[NSNumber numberWithInt:00]];
NSDate *endDate = [NSDate dateWithYear:[NSNumber numberWithInt:2015] month:[NSNumber numberWithInt:30] day:[NSNumber numberWithInt:1] hour:[NSNumber numberWithInt:00] minute:[NSNumber numberWithInt:00] seconds:[NSNumber numberWithInt:59]];
NSPredicate *explicitTimeInterval =
[NSPredicate predicateWithFormat:#"%K >= %# AND %K < %#",
HKPredicateKeyPathEndDate, startDate,
HKPredicateKeyPathStartDate, endDate];
self.healthStore = [[HKHealthStore alloc]init];
Thanks in advance :)

Resources