Does doing:
self = self.init;
return self;
in objective-C have the same effect as:
self.init()
in swift?
For example, in this context:
else if([format.dateFormatType compare: ISO8601DateFormatType] == NSOrderedSame) {
NSString *isoFormat = ISO8601DateFormatType;
NSString *dateFormat = (isoFormat != nil) ? isoFormat : ISO8601DateFormatType;
NSDateFormatter *formatter = [DateFormat CustomDateFormat: dateFormat];
formatter.locale = [NSLocale localeWithLocaleIdentifier: (#"en_US_POSIX")];
formatter.timeZone = [NSTimeZone localTimeZone];
formatter.dateFormat = dateFormat;
NSDate *date = [formatter dateFromString:(string)];
if (date != nil){
return [self initWithTimeInterval: 0 sinceDate: date];
}
else {
self = self.init;
return self;
}
}
The equivalent of self.init() to call a designated or default initializer in the same scope in Objective-C is
if (date != nil) {
return [self initWithTimeInterval: 0 sinceDate: date];
} else {
return [self init];
}
What's the best way to get a total step count for every day recorded in HealthKit.
With HKSampleQuery's method initWithSampleType (see below) I can set a start and end date for the query using NSPredicate, but the method returns an array with many HKQuantitySamples per day.
- (instancetype)initWithSampleType:(HKSampleType *)sampleType
predicate:(NSPredicate *)predicate
limit:(NSUInteger)limit
sortDescriptors:(NSArray *)sortDescriptors
resultsHandler:(void (^)(HKSampleQuery *query,
NSArray *results,
NSError *error))resultsHandler
I guess I can query all recorded step counts and go through the array and calculate the total step count for each day, but I'm hoping for an easier solution as there will be thousands of HKSampleQuery objects. Is there a way to have initWithSampleType return a total step count per day?
You should use HKStatisticsCollectionQuery:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *interval = [[NSDateComponents alloc] init];
interval.day = 1;
NSDateComponents *anchorComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear
fromDate:[NSDate date]];
anchorComponents.hour = 0;
NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];
HKQuantityType *quantityType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
// Create the query
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType
quantitySamplePredicate:nil
options:HKStatisticsOptionCumulativeSum
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);
}
NSDate *endDate = [NSDate date];
NSDate *startDate = [calendar dateByAddingUnit:NSCalendarUnitDay
value:-7
toDate:endDate
options:0];
// Plot the daily step counts over the past 7 days
[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 countUnit]];
NSLog(#"%#: %f", date, value);
}
}];
};
[self.healthStore executeQuery:query];
Port to Swift with no dependency to SwiftDate library
let calendar = NSCalendar.current
let interval = NSDateComponents()
interval.day = 1
var anchorComponents = calendar.dateComponents([.day, .month, .year], from: NSDate() as Date)
anchorComponents.hour = 0
let anchorDate = calendar.date(from: anchorComponents)
// Define 1-day intervals starting from 0:00
let stepsQuery = HKStatisticsCollectionQuery(quantityType: stepsCount!, quantitySamplePredicate: nil, options: .cumulativeSum, anchorDate: anchorDate!, intervalComponents: interval as DateComponents)
// Set the results handler
stepsQuery.initialResultsHandler = {query, results, error in
let endDate = NSDate()
let startDate = calendar.date(byAdding: .day, value: -7, to: endDate as Date, wrappingComponents: false)
if let myResults = results{
myResults.enumerateStatistics(from: startDate!, to: endDate as Date) { statistics, stop in
if let quantity = statistics.sumQuantity(){
let date = statistics.startDate
let steps = quantity.doubleValue(for: HKUnit.count())
print("\(date): steps = \(steps)")
//NOTE: If you are going to update the UI do it in the main thread
DispatchQueue.main.async {
//update UI components
}
}
} //end block
} //end if let
}
healthStore?.execute(stepsQuery)
Modified #sebastianr's answer using core Swift classes, for just for testing I am returning only steps for just one day, once you have more days you can create a dictionary of Dates and step count and return it
func getStepCountPerDay(completion:#escaping (_ count: Double)-> Void){
guard let sampleType = HKObjectType.quantityType(forIdentifier: .stepCount)
else {
return
}
let calendar = Calendar.current
var dateComponents = DateComponents()
dateComponents.day = 1
var anchorComponents = calendar.dateComponents([.day, .month, .year], from: Date())
anchorComponents.hour = 0
let anchorDate = calendar.date(from: anchorComponents)
let stepsCumulativeQuery = HKStatisticsCollectionQuery(quantityType: sampleType, quantitySamplePredicate: nil, options: .cumulativeSum, anchorDate: anchorDate!, intervalComponents: dateComponents
)
// Set the results handler
stepsCumulativeQuery.initialResultsHandler = {query, results, error in
let endDate = Date()
let startDate = calendar.date(byAdding: .day, value: 0, to: endDate, wrappingComponents: false)
if let myResults = results{
myResults.enumerateStatistics(from: startDate!, to: endDate as Date) { statistics, stop in
if let quantity = statistics.sumQuantity(){
let date = statistics.startDate
let steps = quantity.doubleValue(for: HKUnit.count())
print("\(date): steps = \(steps)")
completion(steps)
//NOTE: If you are going to update the UI do it in the main thread
DispatchQueue.main.async {
//update UI components
}
}
} //end block
} //end if let
}
HKHealthStore().execute(stepsCumulativeQuery)
}
Here is a translation that currently works for Swift 2.0, using the SwiftDate library.
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
let startDate = NSDate().beginningOfDay().oneWeekAgo()
let interval = NSDateComponents()
interval.day = 1
let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: NSDate(), options: .StrictStartDate)
let query = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: predicate, options: [.CumulativeSum], anchorDate: NSDate().begginingOfDay(), intervalComponents:interval)
query.initialResultsHandler = { query, results, error in
let endDate = NSDate()
let startDate = NSDate().beginningOfDay().oneWeekAgo()
if let myResults = results{
myResults.enumerateStatisticsFromDate(startDate, toDate: endDate) {
statistics, stop in
if let quantity = statistics.sumQuantity() {
let date = statistics.startDate
let steps = quantity.doubleValueForUnit(HKUnit.countUnit())
print("\(date): steps = \(steps)")
}
}
}
}
healthKitStore.executeQuery(query)
I wrapped mine in a completion block (objective -c). I found what was best was to set the startDate for the query to todays date at midnight. Hope this helps, feel free to copy/paste to get started
-(void)fetchHourlyStepsWithCompletionHandler:(void (^)(NSMutableArray *, NSError *))completionHandler {
NSMutableArray *mutArray = [NSMutableArray new];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDate *startDate = [calendar dateBySettingHour:0 minute:0 second:0 ofDate:[NSDate date] options:0];
NSDate *endDate = [NSDate date]; // Whatever you need in your case
HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
// Your interval: sum by hour
NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];
intervalComponents.hour = 1;
// Example predicate
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum anchorDate:startDate intervalComponents:intervalComponents];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) {
[results enumerateStatisticsFromDate:startDate toDate:endDate
withBlock:^(HKStatistics *result, BOOL *stop) {
if (!result) {
if (completionHandler) {
completionHandler(nil, error);
}
return;
}
HKQuantity *quantity = result.sumQuantity;
NSDate *startDate = result.startDate;
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
formatter.dateFormat = #"h a";
NSString *dateString = [formatter stringFromDate:startDate];
double steps = [quantity doubleValueForUnit:[HKUnit countUnit]];
NSDictionary *dict = #{#"steps" : #(steps),
#"hour" : dateString
};
[mutArray addObject:dict];
}];
if (completionHandler) {
completionHandler(mutArray, error);
}
};
[self.healthStore executeQuery:query];
}
With Updated Swift 2.0 & SwiftDate library.
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
let startDate = NSDate().beginningOfDay
let interval = NSDateComponents()
interval.day = 1
let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: NSDate(), options: .StrictStartDate)
let query = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: predicate, options: [.CumulativeSum], anchorDate: NSDate().beginningOfDay, intervalComponents:interval)
query.initialResultsHandler = { query, results, error in
let endDate = NSDate()
let startDate = NSDate().beginningOfDay
if let myResults = results{
myResults.enumerateStatisticsFromDate(startDate, toDate: endDate) {
statistics, stop in
if let quantity = statistics.sumQuantity() {
let date = statistics.startDate
let steps = quantity.doubleValueForUnit(HKUnit.countUnit())
print("\(date): steps = \(steps)")
}
}
}
}
healthKitStore.executeQuery(query)
I want to compare different times of outlets with ipad current time so what i did was this
if(CT!=nil && startTime!=nil &&endTime!=nil)
{
/* If current time is greater than begin time but less than end time then he can give feedack
*/
if (([CT compare:startTime]== NSOrderedDescending) && ([CT compare:endTime]== NSOrderedAscending) ) {
return YES;
}
else if (([CT compare:startTime]== NSOrderedSame) || ([CT compare:endTime]== NSOrderedSame))
{
return YES;
}
else if (([CT compare:startTime]== NSOrderedDescending) && ([CT compare:endTime]== NSOrderedDescending))
{
return NO; // out of outlet timings
}
This seems to be working good till now but when i change the outlet time to
start time = 18:30PM
end time = 2:00 AM
Then in that case the above code fails, when the ipad clocks at 1:00AM then the first if condition fails so i added this code below
if (([CT compare:startTime]== NSOrderedAscending) && ([CT compare:endTime]== NSOrderedAscending))
{
}
Now as a developer i am sure that if the device clocks at 1:00 AM then it wont fail but what if somebody updated the outlet timing to
start time = 9:00AM
end time = 11:00 AM
and current device time = 8:00 AM
then in the above case my the second code will fail.
what i want is some suggestion as in what i can do in the second if condition
Code that creates CT is :
NSDate *sysDate = [NSDate date];
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"HH:mm:ss"];
NSString *timeString = [df stringFromDate:sysDate];
NSDate *CT = [df dateFromString:timeString];
startTime and endTime are date objects stored in the plist.
Rewrite your conditions as if there were numbers instead of dates and see what they actually do (NSOrderedDescending becomes > "greater than", NSOrderedAscending becomes < "less than"):
if (ctNumber > startNumber && ctNumber < endNumber)
{ ... }
else if (ctNumber == startNumber || ctNumber == endNumber)
{ ... }
else if (ctNumber > startNumber && ctNumber > endNumber)
{ ... }
I hope this way it's obvious the last one is not what you mean at all, and the other condition you tried later was still incorrect. The correct one is (ctNumber < startNumber || ctNumber > endNumber) which is exactly what standalone else would do, so you can just drop it:
if (ctNumber > startNumber && ctNumber < endNumber)
{ ... }
else if (ctNumber == startNumber || ctNumber == endNumber)
{ ... }
else
{ /* otherwise ; everything else ; in all other cases */ ... }
But you really should follow #ILYA2606 advice and compare time intervals (which are numbers) in future to avoid further confusions like this. It will also allow you to use <= and >= and so drop the middle condition:
NSTimeInterval ctNumber = [CT timeIntervalSince1970];
NSTimeInterval startNumber = [startDate timeIntervalSince1970];
NSTimeInterval endNumber = [endDate timeIntervalSince1970];
BOOL ctLiesInsideInterval = (ctNumber >= startNumber && ctNumber <= endNumber);
return ctLiesInsideInterval;
And finally, I have to point out that the date formatter code that creates CT is nonsense and is equivalent to simply using current time:
NSDate* CT = [NSDate date];
Convert NSString to NSDate, compare timeIntervals:
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"HH:mm:ss"];
NSDate *CTDate = [df dateFromString:CT];
NSDate *startDate = [df dateFromString:startTime];
NSTimeInterval CTInterval = [CTDate timeIntervalSince1970];
NSTimeInterval startInterval = [startDate timeIntervalSince1970];
NSTimeInterval deltaInterval = startInterval - CTInterval;
if(deltaInterval < 0){
//Ascending (CT < startTime)
}
else if(deltaInterval > 0){
//Descending (CT > startTime)
}
else{
//Some (CT = startTime)
}
Try this one..
NSDate *currentDate=[NSDate date];
NSTimeInterval daysInSeconds = 60*60;
NSDate *starttime= [currentDate dateByAddingTimeInterval:daysInSeconds];
daysInSeconds = 2*60*60;
NSDate *endtime= [currentDate dateByAddingTimeInterval:daysInSeconds];
if( [starttime compare:currentDate]==NSOrderedAscending ){
//Ascending (CT < startTime)
}
if( [starttime compare:currentDate]==NSOrderedDescending ){
//Descending (CT > startTime)
}
if( [endtime compare:currentDate]==NSOrderedAscending ){
//Ascending (CT < endtime)
}
if( [endtime compare:currentDate]==NSOrderedDescending ){
//Descending (CT > endtime)
}
I need to take a stored NSDate and reliably determine whether it falls within the current moment's hour, day or week. I seem to have hacked together a solution, but not having solved this problem before, am not entirely confident that it's a reliable one.
Will this survive user-set 12 vs 24 hour time? the date formatting guide indicates that this user setting can lead to some unanticipated date behavior: "In iOS, the user can override the default AM/PM versus 24-hour time setting. This may cause NSDateFormatter to rewrite the format string you set."
What about the basic code pattern for this problem? Does this code seem to reliably serve its purpose? I hate to post a "check my code" sort of question, but it's an unfamiliar-enough problem to me, and tricky enough to rigorously test, that it seemed justified. NSDateFormatter is also relatively new to me; another motivation for the question.
NOTE: The main source of my nervousness is that converting dates to strings and then doing a string compare seems an inherently fragile method of solving this problem. But it's the best I could come up with.
Quick reference: the dateFormats I used for each of the three cases were:
dateFormat = #"yyyyMMddHH"; // For "this hour" check
dateFormat = #"yyyyMMdd"; // For "today" check
dateFormat = #"yyyyww"; // For "this week" check
Thanks! Code Follows:
- (BOOL)didThisCycle {
// Case 1: hourly; Case 2: daily; Case 3: weekly
BOOL did = NO;
NSDate *now = [NSDate date];
NSDate *lastDid = [self.didDates lastObject];
if (![lastDid isKindOfClass:[NSDate class]]) { // Crash protection
return NO;
}
int type = [self.goalType intValue];
switch (type) {
case 1:
{
// If hourly check hour
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
formatter.dateFormat = #"yyyyMMddHH";
NSString *nowString = [formatter stringFromDate:now];
NSString *lastDidString = [formatter stringFromDate:lastDid];
if ([nowString isEqualToString:lastDidString]) {
did = YES;
} else {
did = NO;
}
break;
}
case 2:
{
// If daily check day
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
formatter.dateFormat = #"yyyyMMdd";
NSString *nowString = [formatter stringFromDate:now];
NSString *lastDidString = [formatter stringFromDate:lastDid];
if ([nowString isEqualToString:lastDidString]) {
did = YES;
} else {
did = NO;
}
break;
}
case 3:
{
// If weekly check week
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
formatter.dateFormat = #"yyyyww";
NSString *nowString = [formatter stringFromDate:now];
NSString *lastDidString = [formatter stringFromDate:lastDid];
if ([nowString isEqualToString:lastDidString]) {
did = YES;
} else {
did = NO;
}
break;
}
default:
{
did = NO;
break;
}
}
return did;
}
Use the NSDateComponents class, like so:
NSDate *someDate = // whatever
NSDate *now = [NSDate date];
NSDateComponents *thenComponents = [[NSCalendar currentCalendar] components:NSHourCalendarUnit|NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:someDate];
NSDateComponents *nowComponents = [[NSCalendar currentCalendar] components:NSHourCalendarUnit|NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:now];
if([thenComponents year] == [nowComponents year] && [thenComponents month] == [nowComponents month] && [thenComponents day] == [nowComponents day] && [thenComponents hour] == [nowComponents hour])
{
// hooray
}
Remove the “hour” component if you just want to check the day, or remove both that and “day” (and replace with NSWeekCalendarUnit and the -week method) to check the week.
HI,
I need to display date as "15th November 2010" in iPhone SDK.
How do I do that?
Thanks!
You can use a Date Formatter as explained in this post:
// Given some NSDate* date
NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:#"dd MMM yyyy"];
NSString* formattedDate = [formatter stringFromDate:date];
I believe you can simply just put "th" at the end of the dd in the format string. like this:
#"ddth MMM yyy
but I don't have my Mac in front of me to test it out. If that doesn't work you can try something like this:
[formatter setDateFormat:#"dd"];
NSString* day = [formatter stringFromDate:date];
[formatter setDateFormat:#"MMM yyyy"];
NSString* monthAndYear = [formatter stringFromDate:date];
NSString* date = [NSString stringWithFormat:#"%#th %#", day, monthAndYear];
I know I'm answering something old; but I did the following.
#implementation myClass
+ (NSString *) dayOfTheMonthToday
{
NSDateFormatter *DayFormatter=[[NSDateFormatter alloc] init];
[DayFormatter setDateFormat:#"dd"];
NSString *dayString = [DayFormatter stringFromDate:[NSDate date]];
//yes, I know I could combined these two lines - I just don't like all that nesting
NSString *dayStringwithsuffix = [myClass buildRankString:[NSNumber numberWithInt:[dayString integerValue]]];
NSLog (#"Today is the %# day of the month", dayStringwithsuffix);
}
+ (NSString *)buildRankString:(NSNumber *)rank
{
NSString *suffix = nil;
int rankInt = [rank intValue];
int ones = rankInt % 10;
int tens = floor(rankInt / 10);
tens = tens % 10;
if (tens == 1) {
suffix = #"th";
} else {
switch (ones) {
case 1 : suffix = #"st"; break;
case 2 : suffix = #"nd"; break;
case 3 : suffix = #"rd"; break;
default : suffix = #"th";
}
}
NSString *rankString = [NSString stringWithFormat:#"%#%#", rank, suffix];
return rankString;
}
#end
I grabbed the previous class method from this answer: NSNumberFormatter and 'th' 'st' 'nd' 'rd' (ordinal) number endings