Detect [NSTimeZone localTimezone] secondsFromGMT property change - ios

In our app we're using an NSCalendar to process NSDate objects, for example to obtain the day component.
This NSCalendar instance is a singleton object because we do tons of date calculations in a short amount of time and we noticed that creating new instances with [[NSCalendar alloc] initWith...] each time we needed it was consuming too much time.
The problem is, that if you keep an NSCalendar instance throughout the application execution time, this instance keeps the timezone property unchanged, even if the timezone changed.
So, the problem is that we want to detect changes to the timezone and react properly by either:
self.singletonCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]
or
self.singletonCalendar.timezone = [NSTimezone localTimezone];
Possible solutions we're discussing:
Update the timezone property each time the application becomes active.
Use KVO to detect timezone changes (tried this without success).
Thanks,
Nicolás.
Implemented proposed solution by Putz1103:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(significantTimeChangeNotification) name:UIApplicationSignificantTimeChangeNotification object:nil];
- (void)significantTimeChangeNotification
{
NSTimeZone *localTimezone = [NSTimeZone localTimeZone];
[KIDateUtils setCalendarTimezone:localTimezone];
}

I would do a mashup of two options. I would update it on app start (or entering foreground). But for while the app is currently in the foreground you can set a listener for time changes like the answer here:
"UIApplicationDelegate" has a method called "applicationSignificantTimeChange:" that gets called when there is a significant change in the time.

Have your singleton register for the UIApplicationSignificantTimeChangeNotification notification. When this is received, update the NSCalendar instance in your singleton.

Related

How to schedule the execution of code at 9 : 00 AM

My app based on LocalNotifications in iOS 10.
I have a "Note" entity with "Reminder Date" (Daily, Weekly etc.). Every day I want a check: for today I have notes to remind or not. In case - if the user has only weekly-remind notes, how to check on what day of the week need to show notification.
Thanks
I dont think there is any possibility to execute your code on a specified time. Instead if your app is working on that time you can execute anything as you want. Otherwise application will not be in memory.
Use the code below to get the current time:
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSLog(#"%#",[dateFormatter stringFromDate:[NSDate date]]);
And check if time matches 9:00 AM
Not: only when your application is running.
Making a notification from APNS server is possible even if application is not working

UILocalNotification delayed by 1 hour

I've not experienced this problem personally, but it seems that for a number of my users, a notification being set for one time is actually being triggered an hour later.
Here's the code I use to generate the notification:
UILocalNotification *notif = [[UILocalNotification alloc] init];
notif.fireDate = date;
notif.timeZone = [NSTimeZone defaultTimeZone];
notif.alertBody = #"Alert time!";
notif.alertAction = #"Wake me";
[[UIApplication sharedApplication] scheduleLocalNotification:notif];
Fairly standard. The users who've had the problem are on British time, which has daylight savings. I'm wondering if this is an iOS bug of some kind?
I believe the problem was to do with iOS storing a cache of the timeZone. This feature is kind of confusing, as there are 3 different timeZones you can have (and don't worry if half of this confuses you):
[NSTimeZone defaultTimeZone];
Returns the default time zone for the current application. If no default time zone has been set, this method invokes systemTimeZone and returns the system time zone. The default time zone is the one that the application is running with, which you can change (so you can make the application run as if it were in a different time zone).
[NSTimeZone localTimeZone];
Returns an object that forwards all messages to the default time zone for the current application. The local time zone represents the current state of the default time zone at all times. The local time zone adds a level of indirection, it acts as if it were the current default time zone whenever you invoke a method on it.
[NSTimeZone systemTimeZone];
Returns the time zone currently used by the system. If you get the system time zone, it is cached by the application and does not change if the user subsequently changes the system time zone. The next time you invoke systemTimeZone, you get back the same time zone you originally got. You have to invoke resetSystemTimeZone to clear the cached object.
This whole thing baffled me, personally. But that resetSystemTimeZone method seemed interesting:
If the application has cached the system time zone, this method clears that cached object. If you subsequently invoke systemTimeZone, NSTimeZone will attempt to redetermine the system time zone and a new object will be created and cached.
With the possibility of the user moving between time zones, and when some time zones support daylight savings and some don't, and being in mind that Apple themselves have on-going problems with all of this, it seemed like the logical solution would be to make this as non-breakable as possible.
Non-breakable meant that I used systemTimeZone throughout my entire app, and used resetSystemTimeZone on the line before every mention of it.
UILocalNotification *notif = [[UILocalNotification alloc] init];
notif.fireDate = date;
[NSTimeZone resetSystemTimeZone];
notif.timeZone = [NSTimeZone systemTimeZone];
I haven't had a problem with this so far. Hopefully this will help someone.

Will changing an events Time-Zone affect other people on shared events?

If I were to normalize an EKEvent 's startDate property using a NSDateFormatter and setting the time zone with
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"hh:mm a"];
[formatter setTimeZone:[NSTimeZone systemTimeZone]];
event.startDate = [formatter dateFromString:[NSString stringWithFormat:#"%#", [event.startDate]]];
and I later make a change to that event requiring me to call
[self.event.eventStore saveEvent:currentEvent span:EKSpanThisEvent commit:YES error:nil];
Would it override the time-zone on the server, causing issues for the person (whom uses GCal) in another state that originally created that shared calendar event, or is it just a local change?
If it does affect the event, what is the best way for me to make sure the events on the app side always represent the users current time zone, without having to change format the event each time I refer to it on the UI?
Yes the timezone of an event will sync. If an event happens at 9 Mountain time, the event will be displayed as occurring at 9 to anyone whos devices general setiings is set to mountain time. If their device is eastern time, the event will appear to occur at 7. If you change the time zone of the event to Eastern, devices in mountain time will now see the event occurring at 11, and eastern time devices will see the event occurring at 9. So, changing the time zone does not change the time it occurs in its own time zone, but it will change it relative to where you moved it from.
You can do 2 things after asking the user when the event occurs: 1) you can assume the event occurs in the users devices current time zone. 2) you can ask them where the event occurs and set the events time zone to that.
If you have an event with the wrong time, you should first set its time zone to where the event is actually happening, then change when it occurs to when it actually happens, where it actually happens.
If you just focus on getting the data on the event set correctly, it will appear correctly on everyones device who is sharing the calendar with you.

iOS calendar check in background

I would like my app to check on a date while running in the background. At that point, I would like to do a local notification, but I know how to do that... What I am interested in is, if somebody even clicks the home button, and my app is in the background, I would like my app to check what date it is (once a day, while in the background), and if it is a particular date, I would like to do a local notification - a sort of an app-specific calendar...
Any ideas??
Your app (probably) doesn't run in the background so you cannot check the date, but you can schedule a local notification for a specific date (fireDate) with the following
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
localNotif.fireDate = [NSDate dateWithTimeIntervalSinceNow:60*90]; // the date you want the notification to fire.
localNotif.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
For more info see Apple's docs
Maybe this post would be helpful for you http://mobileorchard.com/ios-advanced-programming-understanding-ios-4-multitasking/ . Good luck!

Find out when a repeating UILocalNotification was fired

I have a UILocalNotification that is set to start at 2012-06-18 10:00 with a repeat interval of 1 minute.
At 2012-06-18 10:05 5 notifications would have been triggered. The user would then choose notification number 3. The notification should have been fired at 2012-06-18 10:02.
In my - application:didReceiveLocalNotification: method. How can I programatically get this fire date from notification number 3 which should be 2012-06-18 10:02. I know I can get the intitial/start fireDate property from the UILocalNotification but I'm not interested in that. I'm interested to know the fireDate of this repeating notification (not the intial/start fireDate).
If someone can explain to me how to find out which repeating UILocalNotification was fired without parsing the - description of the notification I will give you some of my hard earned bounty.
So I have given this a bit more thought, and as I said in my comment it doesnt seem possible because of the way notifications are handled.
You create one and fire immediately or schedule it.
A notification is just the storage of some information which means it is only meant to be read for its properties.
The application receives the notification through
application:didReceiveLocalNotification:
In where only the UILocalNotification itself is passed. This notification ONLY has a bunch of properties which were set at the start.
The repeat interval is only used to re-notify the user, it doesnt change what the notification has inside.
This comes to the conclusion that for the behavior you expect to achieve you would have to fire different notifications if you expect to pass different information or perform different actions.
(Sub-classing is also not useful as explained here https://stackoverflow.com/a/8583329/1068522)
The best alternate solution to getting the fireDate of the UILocalNotification is to calculate the date.
Given that you have an initial fireDate, you can use the repeated interval to calculate the fireDate of a given notification.
1.Start with the initial fire date
2.Get the notification number/index of the notification you have
3.Multiply the repeated interval by the index and add it onto your initial fire date
However, date calculating, as mentioned in the below link, is tricky due to time zones and "other nasty things."
Here's a helpful link:
How to grab the NEXT fire date from a UILocalNotification object
And of course, there is the final, fall back solution of parsing the description method. Using [notification.fireDate description] However, as you probably know, it is NEVER a good idea to do so because the formatting may change in the future, thus breaking your code.
Hope this helped!
Edit:
Example: Okay, so say my first initial fireDate was 2012-06-18 10:00
I know that my repeated interval is every ONE minute, right?
So say, that the user taps uilocalnotification number 3, then that means TWO minutes should have passed!
THEREFORE, the time for that specific notification is: 2012-06-18 10:02
Does this make sense?
Q:
If someone can explain to me how to find out which repeating
UILocalNotification was fired without parsing the - description of the
notification I will give you some of my hard earned bounty.
A: The easiest way:
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSDateComponents *comps = [[NSCalendar currentCalendar] components:notification.repeatInterval fromDate:notification.fireDate toDate:[NSDate date] options:0];
NSLog(#"Notification #%d", [comps minute] + 1);
}
Q: How to get that "next fire date" from description without parsing?
A: There is private/undocumented function for this: nextFireDateForLastFireDate
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"next: %#", [notification nextFireDateForLastFireDate:[NSDate date]]);
}
Q: Maybe there is some kind of count in notifications? property or method?
A: There's undocumented properties:
#property (assign, nonatomic) int totalRepeatCount;
#property (assign, nonatomic) int remainingRepeatCount;
But they seem to have always same value: UILocalNotificationInfiniteRepeatCount. Both of them is managed by operating system, so overwriting of this properties or underlying instance variable does nothing. Moreover, nothing changes inside UILocalNotifications from firing to firing, there's no way to distinguish one UILocalNotification from another (except address in memory). Next fireDate is calculated basing on current time.

Resources