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;
}
Related
I have a fairly simple code that gets heart beat, and is not returning anything. No error, no warning, nothing, and nothing in the doc that I could find to help me. Am I missing something obvious ?
Here is the code :
- (void) readLastDayHeartRateMeasurements
{
NSCalendar *calendar = [NSCalendar currentCalendar] ;
NSDate *startDate = [NSDate date] ;
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay
value:-1
toDate:startDate
options:0] ;
HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate] ;
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate
endDate:endDate
options:HKQueryOptionNone] ;
self.query = [[HKSampleQuery alloc] initWithSampleType:sampleType
predicate:predicate
limit:HKObjectQueryNoLimit
sortDescriptors:nil
resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error)
{
NSLog(#"Query %# got a result with %lu samples, error %#",query,(unsigned long)[results count],error) ;
[error logDetailsOfError] ;
}];
if (self.query)
{
NSLog(#"Query %# about to start",self.query) ;
[self.hkStore executeQuery:self.query] ;
NSLog(#"Query %# started",self.query) ;
}
else
{
NSLog(#"No query to execute") ;
}
}
I get the following logs :
2019-01-06 16:56:06.252736+0100 HeartBeatDetails[1128:193491] Authorization succes : 1
2019-01-06 16:56:06.253984+0100 HeartBeatDetails[1128:193491] Query <HKSampleQuery:0x2832c19a0 inactive> about to start
2019-01-06 16:56:06.254440+0100 HeartBeatDetails[1128:193491] Query <HKSampleQuery:0x2832c19a0 activating> started
2019-01-06 16:56:06.276334+0100 HeartBeatDetails[1128:193492] Query <HKSampleQuery:0x2832c19a0 deactivated> got a result with 0 samples, error (null)
I'm running from XCode directly to my phone. As I obviously have hear rates in my phone, what am I doing wrong ?
Thanks,
Shame on me !
The start date and end date are switched...
Correct code :
NSDate *endDate = [NSDate date] ;
NSDate *startDate = [calendar dateByAddingUnit:NSCalendarUnitHour
value:-1
toDate:endDate
options:0] ;
I'm having trouble verifying if an event already exists on the user's calendar. I need to check this to determine if I should add it or not, so that I don't create duplicate calendar entries. Right now, I create a duplicate entry every time I run the code.
First, here is how I am creating the calendar entry:
+ (NSString *) addEventToCalenderWithDate : (NSDate *) eventDate
eventTitle : (NSString *) eventTitle
eventLocation : (NSString *) eventLocation
allDayEvent : (BOOL) isAllDay
{
EKEventStore *store = [[EKEventStore alloc] init];
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (!granted) {
returnValue = #"calendar error";
}
else if ([self eventExists:dateAndTime eventTitle:eventTitle allDayEvent:isAllDay]) {
returnValue = #"duplicate";
}
else {
EKEvent *event = [EKEvent eventWithEventStore:store];
event.title = eventTitle;
event.startDate = dateAndTime;
if (eventTimeString == (id)[NSNull null] || eventTimeString.length == 0 || isAllDay) {
event.allDay = YES;
event.endDate = dateAndTime;
} else {
event.endDate = [event.startDate dateByAddingTimeInterval:60*60]; //set 1 hour meeting
}
event.location = eventLocation;
[event setCalendar:[store defaultCalendarForNewEvents]];
NSError *err = nil;
[store saveEvent:event span:EKSpanThisEvent commit:YES error:&err];
returnValue = #"success";
}
}];
return returnValue;
}
This sets the event correctly. However, if I run it again, I expect that the else if clause will return YES and no new entry will be created. However, it always returns NO and I create a new calendar entry with each execution. Here is that method:
+ (BOOL) eventExists : (NSDate *) date
eventTitle : (NSString *) eventTitle
allDayEvent : (BOOL) isAllDay
{
EKEventStore *store = [[EKEventStore alloc] init];
NSPredicate *predicateForEventOnDate = [[NSPredicate alloc] init];
if (isAllDay)
predicateForEventOnDate = [store predicateForEventsWithStartDate:date endDate:date calendars:nil]; // nil will search through all calendars
else
predicateForEventOnDate = [store predicateForEventsWithStartDate:date endDate:[date dateByAddingTimeInterval:60*60] calendars:nil]; // nil will search through all calendars
NSArray *eventOnDate = [store eventsMatchingPredicate:predicateForEventOnDate];
NSLog(#"eventOnDate: %#", eventOnDate);
BOOL eventExists = NO;
for (EKEvent *eventToCheck in eventOnDate) {
if ([eventToCheck.title isEqualToString:eventTitle]) {
eventExists = YES;
}
}
return eventExists;
}
As I step through this method, I notice that the NSArray called eventOnDate is nil (the EKEventStore is not nil). I don't know if this means that it simply did not find any matching events or if something else is going on.
What am I doing wrong that won't allow this to identify existing events on the calendar? Thank you!
The problem appears to be with the date range you have selected for your predicate.
predicateForEventOnDate = [store predicateForEventsWithStartDate:date endDate:date calendars:nil];
This will look for events within a "0" second range because the start and end date of your predicate query is identical.
predicateForEventOnDate = [store predicateForEventsWithStartDate:date endDate:[date dateByAddingTimeInterval:60*60] calendars:nil];
This will only look for events that lie within an hour of the date provided.
NSCalendar *const calendar = NSCalendar.currentCalendar;
NSCalendarUnit const preservedComponents = (NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay);
//strip away hours, minutes and seconds to find date - at start of day
NSDateComponents *startComponents = [calendar components:preservedComponents fromDate:self.date];
//set finished date to 1 full day later
NSDateComponents *offset = [[NSDateComponents alloc] init];
[offset setDay:1];
NSDate *start = [calendar dateFromComponents:startComponents];
NSDate *finish = [calendar dateByAddingComponents:offset toDate:self.date options:0];
NSPredicate *predicateForEventOnDate = [[NSPredicate alloc] init];
if (isAllDay)
predicateForEventOnDate = [store predicateForEventsWithStartDate:start endDate:finish calendars:nil];
NSArray *eventOnDate = [store eventsMatchingPredicate:predicateForEventOnDate];
This will produce an array that covers events for the full day from start to finish.
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];
}
I'm doing an app that reads daily steps and sleep data from Apple HealthKit.
For Steps, it's pretty easy because it is a HKQuantityType, so I can apply HKStatisticsOptionCumulativeSum option on it. Put the start date, end date, and date interval in, and you got it.
- (void)readDailyStepsSince:(NSDate *)date completion:(void (^)(NSArray *results, NSError *error))completion {
NSDate *today = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [calendar components:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:date];
comps.hour = 0;
comps.minute = 0;
comps.second = 0;
NSDate *midnightOfStartDate = [calendar dateFromComponents:comps];
NSDate *anchorDate = midnightOfStartDate;
HKQuantityType *stepType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKStatisticsOptions sumOptions = HKStatisticsOptionCumulativeSum;
NSPredicate *dateRangePred = [HKQuery predicateForSamplesWithStartDate:midnightOfStartDate endDate:today options:HKQueryOptionNone];
NSDateComponents *interval = [[NSDateComponents alloc] init];
interval.day = 1;
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:stepType quantitySamplePredicate:dateRangePred options:sumOptions anchorDate:anchorDate intervalComponents:interval];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {
NSMutableArray *output = [NSMutableArray array];
// we want "populated" statistics only, so we use result.statistics to iterate
for (HKStatistics *sample in result.statistics) {
double steps = [sample.sumQuantity doubleValueForUnit:[HKUnit countUnit]];
NSDictionary *dict = #{#"date": sample.startDate, #"steps": #(steps)};
//NSLog(#"[STEP] date:%# steps:%.0f", s.startDate, steps);
[output addObject:dict];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (completion != nil) {
NSLog(#"[STEP] %#", output);
completion(output, error);
}
});
};
[self.healthStore executeQuery:query];
}
But for Sleep it's not so straight forward. There are many things I stuck on.
First, unlike steps, sleep is a HKCategoryType. So we cannot use HKStatisticsCollectionQuery to sum it because this method only accepts HKQuantityType.
Also there are 2 value types of sleep, HKCategoryValueSleepAnalysisInBed and HKCategoryValueSleepAnalysisAsleep. I'm not sure which value is best for just the sleep duration. I'll just use HKCategoryValueSleepAnalysisAsleep only for simplicity.
Sleep data comes in an array of HKCategorySample objects. Each with start date and end date. How do I effectively combine those data, trim it to within a day, and get the daily sleep duration (in minutes) out of it? I found this DTTimePeriodCollection class in DateTool pod that may do this job, but I haven't figure it out yet.
Simply put, if anyone knows how to get daily sleep duration using Apple HealthKit, please tell me!
Check how I have did this, its working for me to get collection of the sleep data
func sleepTime() {
let healthStore = HKHealthStore()
// startDate and endDate are NSDate objects
// first, we define the object type we want
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// You may want to use a predicate to filter the data... startDate and endDate are NSDate objects corresponding to the time range that you want to retrieve
//let predicate = HKQuery.predicateForSamplesWithStartDate(startDate,endDate: endDate ,options: .None)
// Get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// the block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 100000, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// Handle the error in your app gracefully
return
}
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
let startDate = sample.startDate
let endDate = sample.endDate
print()
let sleepTimeForOneDay = sample.endDate.timeIntervalSince(sample.startDate)
}
}
}
}
}
This gives the array of entry slots.
I used this:
#import HealthKit;
#implementation HKHealthStore (AAPLExtensions)
- (void)hkQueryExecute:(void (^)(double, NSError *))completion {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
NSDate *startDate = [calendar dateFromComponents:components];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
HKSampleType *sampleType = [HKSampleType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:0 sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!results) {
NSLog(#"An error occured fetching the user's sleep duration. In your app, try to handle this gracefully. The error was: %#.", error);
completion(0, error);
abort();
}
double minutesSleepAggr = 0;
for (HKCategorySample *sample in results) {
NSTimeInterval distanceBetweenDates = [sample.endDate timeIntervalSinceDate:sample.startDate];
double minutesInAnHour = 60;
double minutesBetweenDates = distanceBetweenDates / minutesInAnHour;
minutesSleepAggr += minutesBetweenDates;
}
completion(minutesSleepAggr, error);
}];
[self executeQuery:query];
}
And then in view controller:
- (void)updateUsersSleepLabel {
[self.healthStore hkQueryExecute: ^(double minutes, NSError *error) {
if (minutes == 0) {
NSLog(#"Either an error occured fetching the user's sleep information or none has been stored yet.");
dispatch_async(dispatch_get_main_queue(), ^{
self.sleepDurationValueLabel.text = NSLocalizedString(#"Not available", nil);
});
}
else {
int hours = (int)minutes / 60;
int minutesNew = (int)minutes - (hours*60);
NSLog(#"hours slept: %ld:%ld", (long)hours, (long)minutesNew);
dispatch_async(dispatch_get_main_queue(), ^{
self.sleepDurationValueLabel.text = [NSString stringWithFormat:#"%d:%d", hours, minutesNew] ;
});
}
}];
}
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 :)