HealthKit Read data Heart Rate and BloodPressure 'latest update after sync' - objective-c

I am syncing Heart Rate and BloodPressure data from HealthKit.
The problem with this approach is when the user enters historic data which will not be synced. How do I perform the same query but with CreationDate (instead of StartDate), or some kind of database ID which will identify the historic value as being newer?
I just want to filter out all the newly created values from healthkit.
-(void)getSpecificHealthKitDataHeartReat:(NSDate*)myDate
{
NSDateFormatter *dtFormat = [[NSDateFormatter alloc] init];
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];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
//[HKQuery predicateForObjectWithUUID:(nonnull NSUUID *)]
//Read HeartRate
HKHealthStore *healthStore = [[HKHealthStore alloc] init];
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierStartDate ascending:YES];
HKQuantityType *heartRateType2 = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
HKSampleQuery *sampleQuery2 = [[HKSampleQuery alloc] initWithSampleType:heartRateType2 predicate:predicate limit:0 sortDescriptors:#[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error)
{
if (!results)
{
NSLog(#"There are no heart rate results. The error was: %#.", error);
return;
}
else
{
NSMutableArray *hrArray = [[NSMutableArray alloc]init];
for(HKQuantitySample *samples in results)
{
HKQuantity *hrQuantity = [samples quantity];
// double hr = [hrQuantity doubleValueForUnit:[HKUnit unitFromString:#"count/min"]];
double hr = [hrQuantity doubleValueForUnit: [[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]];
NSLog(#"hr %f",hr);
NSLog(#"startDate %#",samples.startDate);
NSLog(#"endDate %#",samples.endDate);
}
}
}];
// Execute the query
[healthStore executeQuery:sampleQuery2];
}

Use HKAnchoredObjectQuery (documentation here). It is designed for exactly this use case.

Related

Parse this month only

I am new to parse and I need help retrieve objects that are in the current month.
First I have a class called food and have a column of type Date called food_date
I am assuming I have to use greaterThan and less Than, but I am unsure how to.
NSDate *thisMonth = [NSDate date];
PFQuery *foodList = [PFQuery queryWithClassName:#"food"];
[foodList whereKey:#"food_date"
I think you need something like this :
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
components.day = 1;
// Get the date of the first day of the current month
NSDate *minimumDate = [calendar dateFromComponents:components];
components.month = components.month + 1;
// Get the date of the first day of the next month
NSDate *maximumDate = [calendar dateFromComponents:components];
PFQuery *foodList = [PFQuery queryWithClassName:#"food"];
[foodList whereKey:#"food_date" greaterThanOrEqualTo:minimumDate];
[foodList whereKey:#"food_date" lessThan:maximumDate];
[foodList findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d foods.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
I hope that help.

How do I Isolate an Event's Title/Time in a variable to display on Storyboard?

Here's the full description of my problem: I am fetching the events of a calendar for a full day (i.e.today) and storing them in an array. How do I isolate the next relevant (one that has not passed) event's title and time from the array to display them separately as labels?
Here's my code:
//Load Calendar Events
EKEventStore *store = [[EKEventStore alloc] init];
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted,
NSError *error) {
if (granted) {
NSLog(#"User has granted permission");
// Get the appropriate calendar
NSCalendar *calendar = [NSCalendar currentCalendar];
// Create the start date components
NSDateComponents *beginDayComponents = [[NSDateComponents alloc] init];
beginDayComponents.day = 0;
NSDate *todayStart = [calendar dateByAddingComponents:beginDayComponents
toDate:[NSDate date]
options:0];
// Create the end date components
NSDateComponents *endDayComponents = [[NSDateComponents alloc] init];
endDayComponents.day = 0;
NSDate *todayEnd = [calendar dateByAddingComponents:endDayComponents
toDate:[NSDate date]
options:0];
// Create the predicate from the event store's instance method
NSPredicate *predicate = [store predicateForEventsWithStartDate:todayStart
endDate:todayEnd
calendars:nil];
// Fetch all events that match the predicate
NSArray *events = [store eventsMatchingPredicate:predicate];
NSLog(#"Here are the events in the array, %#", events);
} else {
NSLog(#"User has not granted permission");
}
}];
Thanks in advance, and have a good day!
As apple states in its EKEventStore-documentation you will have to sort your array first, so that the next pending event is at index 0.
Note: Retrieving events from the Calendar database does not
necessarily return events in chronological order. To sort an array of
EKEvent objects by date, call sortedArrayUsingSelector: on the array,
providing the selector for the compareStartDateWithEvent: method.
I'd suggest you then just pick the EKEvent-Object at index 0 of your array and read the properties from it and set them on your label.
EKEvent *event = [events objectAtIndex:0];
yourTitleLabel.text = event.text;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateformat = #"dd.MM HH:mm";
yourDateLabel.text = [formatter stringFromDate:event.startDate];
Edit:
You would sort your array events like this:
events = [events sortedArrayUsingSelector:#selector(compareStartDateWithEvent:)];
For this to work, you have to import EventKit/EventKit.h

Why do NSDate instances from NSCalendar and NSDateFormatter have slightly different values?

When passing dates over the wire from client to server and back again, I format the date to a string for JSON using the format #"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" with the #"en_US_POSIX" locale. The same formatter provides an instance of NSDate from the date strings returned from the server to the client.
To test the conversions, I am trying to use NSDateComponents and NSCalendar to generate an independent date to use to validate the date from the formatter. However, the NSDate instances created from the NSCalendar and NSDateComponents vary ever so slightly from the NSDate instances provided by the NSDateFormatter. I do not understand why. Different dates produce a different number of variances. I apologize for yet another NSDate/NSDateFormatter question, but hope you find it somewhat novel.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
dateFormatter.dateFormat = #"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
dateFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSMutableArray *valuesWithError = [NSMutableArray arrayWithCapacity:750];
NSMutableArray *valuesThatMatch = [NSMutableArray arrayWithCapacity:250];
for (NSInteger milliseconds = 0; milliseconds < 1000; milliseconds++) {
NSString *dateString = [NSString stringWithFormat:#"2001-01-01T00:00:00.%03dZ", milliseconds];
NSLog(#"%#", dateString);
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
calendar.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
NSDateComponents *dc = [[NSDateComponents alloc] init];
dc.year = 2001;
dc.month = 1;
dc.day = 1;
dc.hour = 0;
dc.minute = 0;
dc.second = 0;
NSDate *expectedDate = [calendar dateFromComponents:dc];
NSTimeInterval baseInterval = [expectedDate timeIntervalSinceReferenceDate];
double mulitplier = 0.001;
NSTimeInterval millisecondsToAdd = milliseconds * mulitplier;
baseInterval += millisecondsToAdd;
expectedDate = [NSDate dateWithTimeIntervalSinceReferenceDate:baseInterval];
NSDate *dateFromFormat = [dateFormatter dateFromString:dateString];
NSTimeInterval expectedDateFromReferenceDate = [expectedDate timeIntervalSinceReferenceDate];
NSTimeInterval dateFromFormatFromReferenceDate = [dateFromFormat timeIntervalSinceReferenceDate];
NSTimeInterval differenceByTimeIntervalSubtraction = ABS(expectedDateFromReferenceDate - dateFromFormatFromReferenceDate);
NSTimeInterval differenceByTimeIntervalFromDate = [expectedDate timeIntervalSinceDate:dateFromFormat];
if (![expectedDate isEqualToDate:dateFromFormat]) {
NSLog(#"Difference = %e", differenceByTimeIntervalSubtraction);
[valuesWithError addObject:#(milliseconds)];
} else {
[valuesThatMatch addObject:#(milliseconds)];
}
}

Increase NSDate +1 day method Objective-C?

This is my method I use for increasing by one day in my navigationBar, and setting the name of the day as a title. I know it's wrong because I set "today" variable every time its called. But I can't figure out how to increase +1 day every time I call this method.
-(void)stepToNextDay:(id)sender
{
today = [NSDate date];
NSDate *datePlusOneDay = [today dateByAddibgTimeInterval:(60 * 60 * 24)];
NSDateFormatter *dateformatterBehaviour = [[[NSDateFormatter alloc]init]autorelease];
[dateFormatter setDateFormat:#"EEEE"];
NSString *dateString = [dateFormatter stringFromDate:datePlusOneDay];
self.navigationItem.title = datestring;
}
Store the date your are showing in a property (ivar, ...) of your view controller. That way you can retrieve the current setting when you go to the next day.
If you want to reliably add dates, use NSCalendar and NSDateComponents to get a "1 day" unit, and add that to the current date.
NSCalendar* calendar = [[[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar] autorelease];
NSDateComponents* components = [[[NSDateComponents alloc] init] autorelease];
components.day = 1;
NSDate* newDate = [calendar dateByAddingComponents: components toDate: self.date options: 0];
As from iOS 8 NSGregorianCalendar is depricated, the updated answer will be,
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = 1;
NSDate *newDate = [calendar dateByAddingComponents:components toDate:today options:0];
Swift Code:
var today:NSDate = NSDate()
let calender:NSCalendar! = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
var components:NSDateComponents = NSDateComponents()
components.setValue(1, forComponent: NSCalendarUnit.CalendarUnitDay)
var newDate:NSDate! = calender.dateByAddingComponents(components, toDate:today, options: NSCalendarOptions(0))

NSNumber from NSDate

I'm attempting to get around a date validation that refuses to take anything earlier than tomorrow.
So far I have this:
NSDate *dateY = [NSDate dateWithTimeIntervalSinceNow:-86400];
// Negative one day, in seconds (-60*60*24)
NSLog(#"%#", [NSDate date]);
// "yyyy-MM-dd HH:mm:ss Z", accurate assuming Z = +0000
NSLog(#"%#", dateY);
// "yyyy-MM-dd HH:mm:ss Z", same accuracy (minus one day)
That's great, but dateY is not an NSNumber. I need an NSNumber for the comparison, but I can't find anything that works. (I don't even know how an NSNumber can be 2011-04-14 13:22:29 +0000, anyway...)
I can use NSDateFormatter to convert an NSDate into an NSString, so if it would be possible to take that string and convert it to the required NSNumber (as opposed to directly converting the NSDate to an NSNumber, which I can't seem to find help with either), that would be fine.
- (BOOL)validateDueDate:(id *)ioValue error:(NSError **)outError {
NSDate *dateY = [NSDate dateWithTimeIntervalSinceNow:-86400];
NSNumber *tis1970 = [NSNumber numberWithDouble:[dateY timeIntervalSince1970]];
NSLog(#"NSNumber From Date : %#", tis1970);
NSLog(#"Date From NSNumber : %#", [NSDate dateWithTimeIntervalSince1970:[tis1970 doubleValue]]);
// Due dates in the past are not valid
// Enforced that a due date has to be >= today's date
if ([*ioValue compare:[NSDate date]] == NSOrderedAscending) {
if (outError != NULL) {
NSString *errorStr = [[[NSString alloc] initWithString:#"Due date must be today or later."] autorelease];
NSDictionary *userInfoDictionary = [NSDictionary dictionaryWithObject:errorStr forKey:#"ErrorString"];
NSError *error = [[[NSError alloc]
initWithDomain:TASKS_ERROR_DOMAIN
code:DUEDATE_VALIDATION_ERROR_CODE
userInfo:userInfoDictionary] autorelease];
*outError = error;
}
return NO;
} else {
return YES;
}
}
Right now, the user is not allowed to choose a date before tomorrow. errorStr lies. Before today makes more sense than before tomorrow as a rule for refusing to save the date, so I've been fighting with this thing to let me use yesterday in place of today, rather than looking any deeper.
Edit: Using NSOrderedSame allows any date to be selected without an error. That won't do.
You can convert an NSDate to an NSNumber like this:
NSDate *aDate = [NSDate date];
NSNumber *secondsSinceRefDate = [NSNumber numberWithDouble:[aDate timeIntervalSinceReferenceDate]];
and convert back like:
aDate = [NSDate dateWithTimeIntervalSinceReferenceDate:[NSNumber doubleValue]];
All that is needed to get a NSNumber is
NSDate *dateY = [NSDate dateWithTimeIntervalSinceNow:-86400];
NSNumber *tis1970 = [NSNumber numberWithDouble:[dateY timeIntervalSince1970]];
NSLog(#"NSNumber From Date : %#", tis1970);
NSLog(#"Date From NSNumber : %#", [NSDate dateWithTimeIntervalSince1970:[tis1970 doubleValue]]);
You should never use 86400 to calculate date differences, because not all days have 86,400 seconds in them. Use NSDateComponents instead:
- (BOOL)validateDueDate:(NSDate *)dueDate error:(NSError *)error {
NSDate *today = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:today];
//adjust the components to tomorrow at the first instant of the day
[components setDay:[components day] + 1];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
NSDate *tomorrow = [calendar dateFromComponents:components];
NSDate *earlierDate = [dueDate earlierDate:tomorrow];
if ([earlierDate isEqualToDate:dueDate]) {
//the dueDate is before tomorrow
if (error != nil) {
NSString *errorStr = [[[NSString alloc] initWithString:#"Due date must be today or later."] autorelease];
NSDictionary *userInfoDictionary = [NSDictionary dictionaryWithObject:errorStr forKey:NSLocalizedDescriptionKey];
*error = [[[NSError alloc] initWithDomain:TASKS_ERROR_DOMAIN code:DUEDATE_VALIDATION_ERROR_CODE userInfo:userInfoDictionary] autorelease];
}
return NO;
}
return YES;
}
WARNING: code typed in a browser. Caveat Implementor