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.
Related
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.
My app schedules UILocalNotifications to be delivered to its users at varying times of the user's choice.
I'm running into a situation with how to manage the applicationIconBadgeNumber in this scenario.
As we know, you have to set the badge number at the time you create the notification. My problem is that the state of the number of badges can change at any time. Consider this scenario:
1) User gets 3 notifications.
2) User creates a new notification to alert her at a given point of time in the future. This notification carries the value of 1 plus the current value of the application badge (3).
3) User goes about their business. In the process of their business, they clear all 3 notifications (and, thus, badge numbers) they currently have by viewing them or otherwise using the app.
4) After the given amount of time passes, the notification appears in iOS, along with its previously calculated value (4, if you don't remember).
5) The application badge is now, 4 even though the user only has one actual notification.
I have searched up and down, but I cannot find an answer to this question which almost certainly has a simple answer I'm completely missing. How do I solve this quandary?
Since your app cannot look in the future, and know which events you'll handle immediately, and which ones you'll leave 'pending' for a while, there's some trick to do :
When notifications are handled by your app (by tapping on the notification(s), icon, ...), you have to :
get a copy of all pending notifications
'renumber' the badge number of these pending notifications
delete all pending notifications
re-register the copy of the notifications with their corrected badge
numbers again
Also, when your app registers a new notification, it has to check how many notifications are pending first, and register the new notification with with :
badgeNbr = nbrOfPendingNotifications + 1;
Looking at my code, it will get clearer. I tested this, and it's definitely working :
In your 'registerLocalNotification' method you should do this :
NSUInteger nextBadgeNumber = [[[UIApplication sharedApplication] scheduledLocalNotifications] count] + 1;
localNotification.applicationIconBadgeNumber = nextBadgeNumber;
When you handle the notification (appDelegate), you should call the method below, which clears the badge on the icon and renumbers the badges for pending notifications (if there are any)
Note that the next code works fine for 'sequential' registered events. If you would 'add' events in between pending ones, you'll have to 're-sort' these events first. I didn't go that far, but I think it's possible.
- (void)renumberBadgesOfPendingNotifications
{
// clear the badge on the icon
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
// first get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification)
NSArray *pendingNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
// if there are any pending notifications -> adjust their badge number
if (pendingNotifications.count != 0)
{
// clear all pending notifications
[[UIApplication sharedApplication] cancelAllLocalNotifications];
// the for loop will 'restore' the pending notifications, but with corrected badge numbers
// note : a more advanced method could 'sort' the notifications first !!!
NSUInteger badgeNbr = 1;
for (UILocalNotification *notification in pendingNotifications)
{
// modify the badgeNumber
notification.applicationIconBadgeNumber = badgeNbr++;
// schedule 'again'
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}
}
Credits to #Whassaahh
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.
This might not be a difficult question to answer for many of you,but would you let me know why the notification is fired, even though i delete the event for it from DataBase(Core-Data).
Thank You,
Best Regards.
You have to cancelLocalNotification to stop firing the UILocalNotification
EDIT
I added here a method to cancel local notification. You need to maintain an id in the
notification.userInfo(dictionary) for every notifications. if you want to cancel notification pass the id of notification in this method
-(void)cancelNotificationForId:(NSString*)id {
NSArray *notifs = [[UIApplication sharedApplication]scheduledLocalNotifications];
for (UILocalNotification *notification in notifs) {
if ([[notification.userInfo objectForKey:#"alarm_id"] isEqualToString:id]) {
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
}
}
Try this method
You need to remove notification with the help of notification ID.
For more reference regarding the terms mentioned in answers by user2339310 and manujmv, please refer Apple Documentation for Canceling Local Notification. This will clear the concept of UILocalNotification and there cancellation.
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];