Old Local Notifications Firing on iOS - ios

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.

Related

IOS/Objective-C: Is there a way to keep track of notifications before and after they are fired?

I gather that IOS does not give you access to the notifications center in order to keep track of notitifications.
However, many apps do seem to keep track of notifications so that when you see the badge on your home screen or a notification fires and you tap inside the app you can view all the notifications. Linkedin does this for example.
In my case, all the notifications refer to managedobjects in one entity in core data so I tried to hack this by estimating which notifications should have fired given their timestamps in core data. However, this is proving to be an unreliable approach. A fetch that grabbed every item beginning with the first notification since last opened would have to know the time stamp of the first notification since last opened. However, while you know the current time, you don't really know the time of the first notification if you don't know what the first notification since the last opening of the app is.
Has anyone devised a way or know of a library to accurately track notifications? It occurred to me one way might be create an accessible doppelganger of the notificationcenter in core data or an array and keep the notifications there. But that's as far as I've gotten.
To create the cache of future notifications in the notification center, I just fetch anything with a date in the future from core data and add the notification to the notification center:
NSDate *now [NSDate* date];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"event!=nil AND event.length>=1&&starttime>=now"];
//perform fetch code here
NSInteger resultCount = [fetchedObjects count];
UILocalNotification *reminderNotification;
for (int i = 0; i < resultCount; i++) {
reminderNotification = [[UILocalNotification alloc] init];
reminderNotification.fireDate = event.starttime;
reminderNotification.timeZone = [NSTimeZone defaultTimeZone];
reminderNotification.alertAction = #"View Event";
reminderNotification.alertBody = event.event;
reminderNotification.applicationIconBadgeNumber = resultCount+1;
//schedule the notification!
[[UIApplication sharedApplication]
scheduleLocalNotification:reminderNotification];
resultCount++;
}
Thanks in advance for any suggestions.

Update iOS icon badge number

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];
}

cancelLocalNotification doesn't seem to work

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.

How to know UILocalNotification remaining time before fires?

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];

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!

Resources