I would like to know remaining time before UILocalNotification fires and then show it in some UIabel in real time.
How can I do that?
Notification has property fireDate - The date and time when the system should deliver the notification.
So you can use it and current date for calculation remained time.
Following two methods available to get the Registered LocalNotifications, get the Object of it and access FireDate, and calculate your time
To get all the notifications
[[UIApplication sharedApplication] scheduledLocalNotifications];
To get particular notification object if you know Which object to get.
[[UIApplication sharedApplication] scheduleLocalNotification:yourNOtificationObject];
Related
I have a icon badge number update requirement. The app tracks tasks. I want the app to have a badge displaying the number of tasks due on each day. There are basically two cases when the badge number needs to be updated:
Midnight every day.
If new tasks are added or tasks are removed.
I know how to handle the second case. I can set badge number in the applicationResignActive func. However, the midnight automatic update is trick for me. To update the badge number, I need to call a func of the app to count the tasks that due on the day. However, in midnight, the app may be in all possible situations: foreground, background and not running. How can I do this? Thank you.
=====================================
To be clearer with my requirement, I would like the badge number to be updated everyday correctly, even the user never opens the app for a whole day or for consecutive several days. Also, I would try to avoid server side support because the app is a standalone app so far. Much appreciated for any help.
=====================================
Final update: I accepted Vitaliy's answer. However, his answer requires the app to be opened at least once every day. Otherwise, the event won't fire and the badge number cannot be updated.
Also, in my case, every time the app enters background event fires, I have to remove the existing notification and schedule a new one, with the up-to-dated badge number recalculated.
I am still interested in some way to handle the case that the app is not opened every day, how can you make sure the badge number is correct. So far, the easiest way is to setup some server and let it push notifications to the app regularly.
You can achieve it with UILocalNotification:
When app goes to background, calculate exact badge count number for nearest midnight
Schedule UILocalNotification at the nearest midnight with your calculated badge count
You will get notification at midnight, and app's badge count will be updated
Example code:
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Calculate nearest midnight or any other date, which you need
NSDate *nearestMidnight = [self nearestMidnight];
// Create and setup local notification
UILocalNotification *notification = [UILocalNotification new];
notification.alertTitle = #"Some title";
notification.alertBody = #"Some message";
notification.fireDate = nearestMidnight;
// Optional set repeat interval, if user didn't launch the app after nearest midnight
notification.repeatInterval = NSCalendarUnitDay;
// Calculate badge count and set it to notification
notification.applicationIconBadgeNumber = [self calculateBadgeCountForDate:nearestMidnight];
[application scheduleLocalNotification:notification];
}
In the following code I check whether the current task (the one being edited) has a localNotification. If so, I try to cancel that notification. Then (if the fireDate hasn't already passed) I update the alertBody of the notification with the (possibly) changed information and re-schedule it.
The problem is that if I create a notification and then edit it, I end up with two notifications going off. Somehow the original one isn't getting canceled...
if ([currentTask localNotification])
{
[[UIApplication sharedApplication] cancelLocalNotification:[currentTask localNotification]];
if ([[NSDate date] compare:[currentTask localNotification].fireDate] == NSOrderedAscending)
{
NSLog(#"current task name: %#", [currentTask name]);
NSString *temp = [NSString stringWithFormat:#"Alert:\n%#", [currentTask name]];
[[currentTask localNotification] setAlertBody:temp];
[[UIApplication sharedApplication] scheduleLocalNotification:[currentTask localNotification]];
}
else
[currentTask setLocalNotification:nil];
}
Any insights as to the problem here?
I know that a copy is made of the notification when it's scheduled - how does cancelLocalNotification find the correct copy? Does it do it by testing the properties for equality? Or does it make a copy of the pointer address and match that up? In other words, if one of the properties is changed, will cancelLocalNotification not be able to match it with the notification that was originally scheduled?
I'll add my findings here for iOS 9. Previously I had code that went like so:
UILocalNotification* notification = getNotification();
[[UIApplication sharedApplication] cancelLocalNotification: notification];
notification.alertBody = newBody;
[[UIApplication sharedApplication] scheduleLocalNotification: notification];
Worked great before iOS 9, cancelling the existing instance and then rescheduling it with a new alertBody or soundName. Then along comes iOS 9 and suddenly running this code doesn't actually cancel the previous notification and so I was ending up with more notifications than I wanted!
The fix for me was to not be lazy in reusing the old notification so I now create a new notification and populate it with the fields of the old notification. Works a treat!
EDIT: I still seem to be getting problems with [[UIApplication sharedApplication] cancelLocalNotification: notification] not working in other situations... I've got my notifications automatically repeating themselves every minute and it seems there might be a bug in Xcode 7.0 meaning its sometimes ignored: https://forums.developer.apple.com/thread/9226
EDIT II: Looks like repeating local notifications scheduled in the app before it was built against iOS 9 can't be cancelled.. at least not easily... still working on finding a solution to this as my app is centered around repeating local notifications for reminders!
EDIT III: I've managed to get rid of a lingering, un-cancellable notification by force quitting the app after an attempt to cancel it... and that seems to have worked... it's not a great solution, certainly not something I want to get my users to do if there's some kind of alternative... I've posted a separate question to see if anyone has any further ideas.
I came across the same issue. the solution i used was.
// Sort the notification on the fire date of the notification.
NSSortDescriptor * fireDateDesc = [NSSortDescriptor sortDescriptorWithKey:#"fireDate" ascending:YES];
//get all scheduled notifications.
NSArray * notifications = [[[UIApplication sharedApplication] scheduledLocalNotifications] sortedArrayUsingDescriptors:#[fireDateDesc]];
// Cancel all scheduled notifications.
[[UIApplication sharedApplication] cancelAllLocalNotifications];
for (int i=0; i<[notifications count]; i++){
UILocalNotification* oneEvent = [notifications objectAtIndex:i];
// update notification data here an then schedule the notification.
[[UIApplication sharedApplication] scheduleLocalNotification:oneEvent];
}
Canceling UILocalNotifications one by one to update something in the notification produces an issue that sometimes the notification does not get cancelled.
So the solution i have implemented is:
1: get all scheduled notifications and store them in whatever you like.
2: Then cancel all scheduled notifications.
3: From step one loop through the notifications, update whatever you want and schedule notifications on the go.
hope this helps.
It looks like if any of the properties of a UILocalNotification have been changed since it was scheduled, that cancelLocalNotification won't work. That makes me think that cancelLocalNotification must check equality of the different properties against the currently scheduled copies of notifications to see which one is a match.
i can get list of my scheduledLocalNotifications in my table view with this code perfectly ;
NSArray *notificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications];
but the only problem is , my local notifications are firing on correct time but showing with wrong time zone on the list.
I'm also using systemTimeZone for scheduling my local notification like this ;
notification.timeZone = [NSTimeZone systemTimeZone];
What is the correct way to get a list of scheduledLocalNotifications with the correct time zone?
Thanks in advance
I've had the same problem when after i had already scheduled a notification at 3:30 pm and it was correctly scheduled when i NSLog it's fireDate it'll show up in a different time zone and that's how i fixed it.
[localNotification.fireDate descriptionWithLocale:NSGregorianCalendar]
this will return fire date with the Gregorian local (or you should use whatever local you have used when scheduling your local notification)
I have a set of UILocalNotifications that I am scheduling. They are firing on time and seem to be working fine. However, on occasion, when a NEW notification fires there are multiple (sometimes only one) OLD notifications that fire along with it.
For example, I schedule a UILocalNotification to take out the trash on Monday at 5pm with no repeat interval. It fires no problem and on time. On Tuesday, I have a UILocalNotification to bring in the trash bins for Tuesday at 5pm, again with no repeat interval. When that one fires, I will see the correct notification for NOW, but also below the current notification will be another notification to take the trash out 1 Day Ago. I have not rescheduled this notification. It appears to be the notification from yesterday.
It is very bizarre and I cannot reproduce it on any sort of consistent basis. I thought that maybe there were some old notifications being added somehow so I added some logic to run through all scheduled notifications and remove any that had a fire date that was in the past but that did not help. Anyone else ever see this problem? Is there some manual clearing of a notification that I need to do when one fires?
EDIT: Added some of the scheduling code
//only schedule if the alert date is later than now
if ([AMDateUtil isNowEarlierThanDate:alertDate]) {
//create the notification and setup some properties
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = alertDate;
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.alertAction = nil;
localNotification.soundName = UILocalNotificationDefaultSoundName;
//add the local notification
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
We had the same issue, but managed to fix it by batching our calls to the API. Instead of calling scheduleLocalNotification each time, instead build an array of all notifications you want to be scheduled. Like this:
NSMutableArray *notifications = [[[UIApplication sharedApplication] scheduledLocalNotifications] mutableCopy];
// add notifications to the mutable array
// same goes for removing notifications
[[[UIApplication sharedApplication] setScheduledLocalNotifications:notifications];
Also make sure you are calling it from the main thread.
Our problems seem to have gone away after doing this.
The first thing that I can think of is to check the repeatInterval of your notifications. It seems that you may want your repeatInterval to be weekly, but the interval seems to be set to daily. To set the repeatInterval to weekly use:
localNotification.repeatInterval = NSWeekCalendarUnit;
Perhaps in some places you may be accidentally using
localNotification.repeatInterval = NSWeekdayCalendarUnit;
which repeats once a day.
If that is not the issue, then perhaps if you post some of your code where you schedule the notifications someone could help you. If a notification has a repeat interval of 0 (or not repeating) then you should not have to manually clear it. If the repeat interval is not 0 then you will have to manually clear it to get it to stop repeating.
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.