48

I have a problem with my UILocalNotification.

I am scheduling the notification with my method.

- (void) sendNewNoteLocalReminder:(NSDate *)date  alrt:(NSString *)title
{
    // some code ...
    UILocalNotification *localNotif = [[UILocalNotification alloc] init]; 

    if (localNotif == nil)  
        return;

    localNotif.fireDate = itemDate; 
    localNotif.timeZone = [NSTimeZone defaultTimeZone];
    localNotif.alertAction = NSLocalizedString(@"View Details", nil); 
    localNotif.alertBody = title;
    localNotif.soundName = UILocalNotificationDefaultSoundName; 
    localNotif.applicationIconBadgeNumber = 0;

    NSDictionary *infoDict = [NSDictionary dictionaryWithObject:stringID forKey:@"id"]; 
    localNotif.userInfo = infoDict; 

    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif]; 
    [localNotif release];
}

Its work fine and I'm correctly receiving the notification. The problem is when I should cancel the notification. Im using this method.

- (void) deleteNewNoteLocalReminder:(NSString*) reminderID noteIDe:(NSInteger)noteIDE
{
    [[UIApplication sharedApplication] cancelLocalNotification:(UILocalNotification *)notification ????  
}

Im not sure what to do here, but my questions are:

How do I know which UILocalNotification object I should delete?
Is there a way to list all notifications?

The only thing I have is the ID of which reminder I should delete.
I was thinking about to save the UILocalNotification object in my "Note" object and get it that way, and when I saving to my SQLite database serialize the object and so on ... is there a smarter way?

P.J.Radadiya
  • 1,493
  • 1
  • 12
  • 21
f0rz
  • 1,495
  • 2
  • 14
  • 26

10 Answers10

92

My solution is to use the UILocalNotification userInfo dictionary. In fact what I do is to generate a unique ID for each of my notifications (of course this ID is something I'm able to retrieve later), then when I want to cancel the notification associated to a given ID I will simply scan all available notifications using the array:

[[UIApplication sharedApplication] scheduledLocalNotifications]

and then I try to match the notifications by investigating the ID. E.g.:


NSString *myIDToCancel = @"some_id_to_cancel";
UILocalNotification *notificationToCancel=nil;
for(UILocalNotification *aNotif in [[UIApplication sharedApplication] scheduledLocalNotifications]) {
  if([[aNotif.userInfo objectForKey:@"ID"] isEqualToString:myIDToCancel]) {
     notificationToCancel=aNotif;
     break;
  }
}
if(notificationToCancel) [[UIApplication sharedApplication] cancelLocalNotification:notificationToCancel];

I don't know if this approach is better or not with respect to the Archiving/Unarchving one, however it works and limits data to be saved to just an ID.

Edit: there was a missing braket

Almas Adilbek
  • 4,371
  • 10
  • 58
  • 97
viggio24
  • 12,316
  • 5
  • 41
  • 34
  • I tried your method and didn't work for me. could you tell me what's wrong ? http://stackoverflow.com/questions/9139586/cancel-local-notification-not-working – Farini Feb 04 '12 at 08:45
  • 2
    You're not using my method in your solution. My solution passes around an ID, while you use the whole object. It seems from your code that there is some early dealloc of the notification object and that's why the "unrecognized selector" (this typically happens when the old object location has been reused by another kind of object). Try to get the class name of the object just before sending the cancelNotification message to dig on it, this may help in debugging. Besides when you assign "theNotification" use "self.theNotification = ..." instead of "theNotificaiton = ..." – viggio24 Feb 04 '12 at 09:47
  • Thank you so much ! You actually inspired me to add a button on every cell where I can: cancelLocalNotification atindex [sender tag] and it works so much better then bringing a uialertview. As you said the name of the object is now just before cancelling and it works just fine! – Farini Feb 04 '12 at 20:03
  • Yes, I have seen your "self-answer" in your question. I agree with you that adding the button and then canceling the notification on its selector is perfectly valid for your specific case. – viggio24 Feb 04 '12 at 20:45
  • If using `CoreDate` I found it helpful to use the unique managed object ID: `NSManagedObjectID *moID = [managedObject objectID];`. Since `CoreData` only assigns a permanent ID after doing the save you can use: `BOOL isTemporary = [[managedObject objectID] isTemporaryID];` – d.ennis Apr 05 '12 at 17:57
  • Note that this works for _scheduled_ notifications only; you can't seem to cancel a notification presented via `presentLocalNotificationNow:`. – Nick Farina Jun 12 '14 at 00:09
  • Any fix for iOS 10 notifications? – Danpe Sep 21 '16 at 14:16
  • With iOS 10 UILocalNotification has been deprecated. You need to use the UNNotificationRequest class which supports a "identifier" field. This identifier can be cached (or retrieved using the getPendingNotificationRequestsWithCompletionHandler: method) and notifications can be selectively removed using the removePendingNotificationRequestsWithIdentifiers: method. – viggio24 Sep 22 '16 at 12:13
57

You can get a list of all scheduled notifications from scheduledLocalNotifications or you can cancel them all:

  [[UIApplication sharedApplication] cancelAllLocalNotifications];
progrmr
  • 75,956
  • 16
  • 112
  • 147
  • 2
    I know about the cancelAllLocalNotifications but I just want to cancel a specific. ScheduledLocalNotifications maybe do the work .... I will try. – f0rz Jul 01 '10 at 13:47
13

My solution is to archive UILocalNotification object you have scheduled with NSKeyedArchiver and store it somewhere (in a plist is preferred). And then, when you want to to can cancel the notification, look up the plist for the correct data and use NSKeyedUnarchiver to unarchive. The code is pretty easy:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:notice];

and

UILocalNotification *notice = [NSKeyedUnarchiver unarchiveObjectWithData:data];
hiepnd
  • 821
  • 4
  • 14
  • You can also serialize the NSData from archievedDataWithRootObject with Base64 encoding and then save into a JSON object that you already have. – Rafael Sanches Apr 28 '12 at 17:03
  • @RafaelSanches sorry for the thread necro. But what's the advantage of using a JSON object if the objective is to store/retrieve uilocalnotification for later deletion? – iamdavidlam May 04 '15 at 09:10
6

My solution is you when you create UILocalNotification at that time you create one NSMutableDictionary and store that notification as a value for key as your ID and put this NSMutableDictionay to your NSUserDefaults

So when you want to cancel any particular localnotification at that time you write [dictionary valueforkey @"KEY"] where as a key you pass your id so you get that particular local notification and pass it to

 [[UIApplication sharedApplication] scheduleLocalNotification:localNotif]; 
Robin
  • 10,011
  • 5
  • 49
  • 75
Mahek
  • 233
  • 1
  • 3
  • 7
5

Swift 5:

UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: arrayContainingIdentifiers)
  • 1
    In case any Xamarin dev would look for the answer. This one worked neatly for me. `UNUserNotificationCenter.Current.RemovePendingNotificationRequests(new string[] { NotificationId });` – Sergey Ivanov Feb 18 '21 at 14:16
3

In swift, you first add a dict to notifications userInfo by:

let dict:NSDictionary = ["ID" : "someString"]
notification.userInfo = dict as! [String : String]

Then, you want to get an array of existing notifications (2), and iterate through each one (3) until you find the one you're looking for. Once you find it, cancel it (4).

// 1. Pass in the string you want to cancel
func cancelLocalNotification(uniqueId: String){

    // 2. Create an array of notifications, ensuring to use `if let` so it fails gracefully
    if let notifyArray = UIApplication.sharedApplication().scheduledLocalNotifications {

        // 3. For each notification in the array ...
        for notif in notifyArray as [UILocalNotification] {
            // ... try to cast the notification to the dictionary object
            if let info = notif.userInfo as? [String: String] {

                // 4. If the dictionary object ID is equal to the string you passed in ...
                if info["ID"] == uniqueId {
                    // ... cancel the current notification
                    UIApplication.sharedApplication().cancelLocalNotification(notif)
                }
            }
        }
    }
}
bwash70
  • 1,135
  • 2
  • 11
  • 24
2

Swift version:

UIApplication.sharedApplication().cancelAllLocalNotifications()
fatihyildizhan
  • 8,614
  • 7
  • 64
  • 88
1

Swift version to cancel particular "Local Notification" using userinfo.:

func cancelLocalNotification(UNIQUE_ID: String){

        var notifyCancel = UILocalNotification()
        var notifyArray = UIApplication.sharedApplication().scheduledLocalNotifications

        for notifyCancel in notifyArray as! [UILocalNotification]{

            let info: [String: String] = notifyCancel.userInfo as! [String: String]

            if info[uniqueId] == uniqueId{

                UIApplication.sharedApplication().cancelLocalNotification(notifyCancel)
            }else{

                println("No Local Notification Found!")
            }
        }
    }
Sohil R. Memon
  • 9,404
  • 1
  • 31
  • 57
0

My solution is to remove all scheduled Notifications on these methods.

-applicationDidFinishLaunchingWithOptions: 
- (void)applicationDidBecomeActive:(UIApplication *)application;
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

with

[[UIApplication sharedApplication] cancelAllLocalNotifications];

You can still get to the LocalNotification object that activated your app by using.

    UILocalNotification *localNotif = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey];

Then I reschedule new Notifications on

- (void)applicationDidEnterBackground:(UIApplication *)application;

This avoids to have bookkeeping for all the notifications. I send some context information in the userInfo dictionary as well. This then helps to jump to the right place in the app.

orkoden
  • 18,946
  • 4
  • 59
  • 50
0

Thank @viggio24 for the good answer, this is a swift version

var notifyCancel = UILocalNotification()
var notifyArray=UIApplication.sharedApplication().scheduledLocalNotifications
var i = 0
while(i<notifyArray.count){
    if let notify = notifyArray[i] as? UILocalNotification{
        if let info = notify.userInfo as? Dictionary<String,String> {
            if let s = info["name"] {
                if(name==s){//name is the the one you want cancel
                    notifyCancel = notify
                    break
                }
            }
        }
    }
i++
}
David Wang
  • 934
  • 1
  • 12
  • 16
  • No need for while and if let notify, just keep it short with `for notify in notifyArray as [UILocalNotification] { if let info = notifier.userInfo as? Dictionary {...}}` – Nitro.de Apr 01 '15 at 13:54