94

I am developing an iPhone alarm app based on local notifications.

On deleting an alarm, the related local notification should get cancelled. But how can I determine exactly which object from the array of local notifications is to be cancelled?

I am aware of [[UIApplication sharedApplication] cancelLocalNotification:notification] method but how can I get this 'notification' to cancel it?

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Yogi
  • 3,578
  • 3
  • 35
  • 56

14 Answers14

219

You can save a unique value for key in your local notification's userinfo. Get all local notification, loop through the array and delete the particular notification.

Code as follows,

OBJ-C:

UIApplication *app = [UIApplication sharedApplication];
NSArray *eventArray = [app scheduledLocalNotifications];
for (int i=0; i<[eventArray count]; i++)
{
    UILocalNotification* oneEvent = [eventArray objectAtIndex:i];
    NSDictionary *userInfoCurrent = oneEvent.userInfo;
    NSString *uid=[NSString stringWithFormat:@"%@",[userInfoCurrent valueForKey:@"uid"]];
    if ([uid isEqualToString:uidtodelete])
    {
        //Cancelling local notification
        [app cancelLocalNotification:oneEvent];
        break;
    }
}

SWIFT:

var app:UIApplication = UIApplication.sharedApplication()
for oneEvent in app.scheduledLocalNotifications {
    var notification = oneEvent as UILocalNotification
    let userInfoCurrent = notification.userInfo! as [String:AnyObject]
    let uid = userInfoCurrent["uid"]! as String
    if uid == uidtodelete {
        //Cancelling local notification
        app.cancelLocalNotification(notification)
        break;
    }
}

UserNotification:

If you use UserNotification (iOS 10+), just follow this steps:

  1. When creating the UserNotification content, add an unique identifier

  2. Remove specific pending notification using removePendingNotificationRequests(withIdentifiers:)

  3. Remove specific delivered notification using removeDeliveredNotifications(withIdentifiers:)

For more info, UNUserNotificationCenter

KingofBliss
  • 15,055
  • 6
  • 50
  • 72
  • @kingofBliss,can u please tell me to give there at "uidtodelete".because it is undeclared in my case. – ishhhh Aug 31 '11 at 04:57
  • @ishhh itsjust a strig value.. you should declare it and intialize it with a value of the uid to be delete – KingofBliss Sep 02 '11 at 07:21
  • @kingofBliss,the uid is always showing null in NSLog.dont konw how to get rid of this.Please help me – ishhhh Oct 07 '11 at 05:07
  • @ishhh have you stored any value for uid in the userinfo dictionary when creating local notification? I think you have missed out that. – KingofBliss Oct 07 '11 at 06:23
  • @kingofBliss, the "uid" it's a name of your own variable, you can use any significant name like "notificationID", and store that in a `NSDictionary` with the value of the id of the entity related to the `UILocalNotification`. Then set the notification.userInfo property to the dictionary with your custom data. Now when you get the notifications you can distinct them with that custom id or anything more you can need. – IgniteCoders Mar 26 '14 at 09:56
  • For Swift, `notifcation.userInfo!` should be casted to `[String:AnyObject]`, instead of `[[String:AnyObject]]`. Can't edit the answer, as SO wants more 4 characters to be edited. – Dhruv Ramani Jun 30 '15 at 14:51
  • after doing this, can you use the id of the cancelled notification to create a new one? – Yanki Twizzy Sep 25 '15 at 20:54
  • @KingofBliss this would only return local notifications created by the app running this code correct? IE if I wanted to delete every notification created by an app could I use var app:UIApplication = UIApplication.sharedApplication() for oneEvent in app.scheduledLocalNotifications { var notification = oneEvent as UILocalNotification app.cancelLocalNotification(notification) – humanbeing Oct 01 '15 at 04:22
  • i am getting eventarray null .. :( what is solution for that – Monika Patel Dec 15 '15 at 03:07
  • @Stela are you sure that the app has notifications yet to trigger? – KingofBliss Dec 17 '15 at 09:57
  • Can you please tell me, how to create array of local notifications and associate with an id ? – Vasanth Jan 16 '19 at 12:58
23

Other Option:

First of All, when you create local notification, you can store it in user defaults for future use, Local notification object can not be stored directly in user defaults, This object needs to be converted into NSData object first, and then NSData can be stored into User defaults. Below is code for that:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:localNotif];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:[NSString  stringWithFormat:@"%d",indexPath.row]];

After you have stored and scheduled local notification, In future, requirement may arise that you need to cancel any of notification that you created earlier, So you can retrieve it from User defaults.

NSData *data= [[NSUserDefaults standardUserDefaults] objectForKey:[NSString   stringWithFormat:@"%d",UniqueKey]];

UILocalNotification *localNotif = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"Remove localnotification  are %@", localNotif);
[[UIApplication sharedApplication] cancelLocalNotification:localNotif];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[NSString stringWithFormat:@"%d",UniqueKey]];

Hope This helps

Sufian
  • 6,405
  • 16
  • 66
  • 120
jigneshbrahmkhatri
  • 3,627
  • 2
  • 21
  • 33
  • Thanks,I have implemented it by first way but your answer is also correct.I will take this into consideration.Can you please tell which one is more efficient?Thanks for the help:) – Yogi Jun 28 '11 at 04:27
  • 1
    @Yogi: If you look at first answer, you need to run for loop everytime if you want to cancel local notification, but in the above answer, you will not need to run any for loop, you can directly access local notification and cancel that local notification and remove it from User defaults, As per my answer, it is more efficient way – jigneshbrahmkhatri Jun 28 '11 at 08:59
  • @JigneshBrahmkhatri Your method is effective. But it will fail when the user uninstall the app, and reinstall it. – KingofBliss Oct 15 '12 at 04:35
  • @KingofBliss, in that case we have to cancel all the notifications, right? So I guess this solution is faster. :) – Sufian Jan 15 '13 at 07:32
  • @Sufian To cancel all notification there is a much faster way [[UIApplication sharedApplication] cancelAllLocalNotifications]; ;) – KingofBliss Jan 16 '13 at 04:06
  • @KingofBliss, yes that is what I was saying. We always cancel all notifications when the app is reinstalled, so I like Jignesh's solution. :) – Sufian Jan 16 '13 at 05:37
  • *We always = We can always. – Sufian Jan 16 '13 at 06:42
  • @Sufian Yes i didnt say this solution is not good. The thing is that when the user reinstall the app, all the previous local notification wil be there(may be a weird bug in ios), but the user defaults will be deleted. So we cant handle that situation with this solution. – KingofBliss Jan 16 '13 at 07:08
  • In that case, we have delete the notifications (created by our app in previous install) by cancelAllLocalNotifications (as you had rightly said). So, this solution doesn't have any future problems which I can see. :) – Sufian Jan 16 '13 at 07:40
8

Here is what i do.

When creating your notification do this:

  // Create the notification

UILocalNotification *notification = [[UILocalNotification alloc]  init] ;



notification.fireDate = alertDate;
notification.timeZone = [NSTimeZone localTimeZone] ;
notification.alertAction = NSLocalizedString(@"Start", @"Start");
notification.alertBody = **notificationTitle**;
notification.repeatInterval= NSMinuteCalendarUnit;

notification.soundName=UILocalNotificationDefaultSoundName;
notification.applicationIconBadgeNumber = 1;

[[UIApplication sharedApplication] scheduleLocalNotification:notification] ;

when trying to delete it do this:

 NSArray *arrayOfLocalNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications] ;

for (UILocalNotification *localNotification in arrayOfLocalNotifications) {

    if ([localNotification.alertBody isEqualToString:savedTitle]) {
        NSLog(@"the notification this is canceld is %@", localNotification.alertBody);

        [[UIApplication sharedApplication] cancelLocalNotification:localNotification] ; // delete the notification from the system

    }

}

This solution should work for multiple notifications, and your not managing any arrays or dictionaries or user defaults. Your simply using the data you've already saved to the systems notification database.

Hope this helps future designers and developers.

Happy coding guys! :D

abhi
  • 401
  • 6
  • 11
  • Thanks for sharing your answer but how this logic works if all of your notifications are having same body or if the body is to be taken from the user.In that case the user can give same body to multiple notifications. – Yogi May 22 '12 at 06:05
  • @Yogi, like alertbody,you can check,notification.firedate to get the required notification. thanks to abhi for a simple solution.upvote 1 for u – Nazik Aug 28 '12 at 05:39
  • 1
    @NAZIK:Thanks for your interest in discussion. But still the user can schedule two notifications on same fire date as it is an alarm application. At least it can be a test case for a tester and this solution looks to be failing there. – Yogi Aug 28 '12 at 07:29
  • @Yogi,wise testing,why cant we check if ([localNotification.alertBody isEqualToString:savedTitle] || [localNotification.firedate ==something]), since the two notifications with same date should contain different alertBody – Nazik Aug 28 '12 at 07:34
  • Do not abuse the `alertBody` or `fireDate` for identifying a notification; use the `userInfo` field for doing this, as the answer by @KingOfBliss details... – severin Apr 17 '15 at 14:18
  • From where we get savedtitle if the class context is different & we not saved the title to NSUserDefaults or some where else in db? – Mohammad Sadiq Shaikh Jan 13 '16 at 10:27
8

Scheduling and removeNotification in swift:

    static func scheduleNotification(notificationTitle:String, objectId:String) {

    var localNotification = UILocalNotification()
    localNotification.fireDate = NSDate(timeIntervalSinceNow: 24*60*60)
    localNotification.alertBody = notificationTitle
    localNotification.timeZone = NSTimeZone.defaultTimeZone()
    localNotification.applicationIconBadgeNumber = 1
    //play a sound
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    localNotification.alertAction = "View"
    var infoDict :  Dictionary<String,String!> = ["objectId" : objectId]
    localNotification.userInfo = infoDict;

    UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
    static func removeNotification(objectId:String) {
    var app:UIApplication = UIApplication.sharedApplication()

    for event in app.scheduledLocalNotifications {
        var notification = event as! UILocalNotification
        var userInfo:Dictionary<String,String!> = notification.userInfo as! Dictionary<String,String!>
        var infoDict :  Dictionary = notification.userInfo as! Dictionary<String,String!>
        var notifcationObjectId : String = infoDict["objectId"]!

        if notifcationObjectId == objectId {
            app.cancelLocalNotification(notification)
        }
    }



}
Roman Barzyczak
  • 3,785
  • 1
  • 30
  • 44
  • 1
    Do not abuse the `alertBody` or `fireDate` for identifying a notification; use the `userInfo` field for doing this, as the answer by @KingOfBliss details... – severin Apr 17 '15 at 14:19
  • Yes alertBody isn't good option for identyfaing a notificaiton. I changed it to userInfo – Roman Barzyczak May 02 '15 at 12:21
7

Swift 4 solution:

UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
  for request in requests {
    if request.identifier == "identifier" {
      UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["identifier"])
    }
  }
}   
swiftache
  • 60
  • 9
Nupur Sharma
  • 1,106
  • 13
  • 15
6

iMOBDEV's solution works perfectly to remove a specific notification (e.g. after deleting the alarm) but it's specially useful when you need to selectively remove any notification that has already fired and is still on the notification center.

A possible scenario would be: the notification for an alarm fires, but the user opens the app without tapping on that notification and schedules that alarm again. If you want to make sure only one notification can be on the notification center for a given item/alarm, it's a good approach. It also allows you not having to clear all notifications every time the app is opened, shall that fit the app better.

  • Upon creating a local notification, use NSKeyedArchiver to store it as Data in UserDefaults. You can create a key equal to what you're saving in the notification's userInfo dictionary. If it's associated with a Core Data object, you could use its unique objectID property.
  • Retrieve it with NSKeyedUnarchiver. Now you're able to delete it using the cancelLocalNotification method.
  • Update the key on UserDefaults accordingly.

Here's a Swift 3.1 version of that solution (for targets below iOS 10):

Store

// localNotification is the UILocalNotification you've just set up
UIApplication.shared.scheduleLocalNotification(localNotification)
let notificationData = NSKeyedArchiver.archivedData(withRootObject: localNotification)
UserDefaults.standard.set(notificationData, forKey: "someKeyChosenByYou")

Retrieve and delete

let userDefaults = UserDefaults.standard
if let existingNotificationData = userDefaults.object(forKey: "someKeyChosenByYou") as? Data,
    let existingNotification = NSKeyedUnarchiver.unarchiveObject(with: existingNotificationData) as? UILocalNotification {

    // Cancel notification if scheduled, delete it from notification center if already delivered    
    UIApplication.shared.cancelLocalNotification(existingNotification)

    // Clean up
    userDefaults.removeObject(forKey: "someKeyChosenByYou")
}
Rygen
  • 309
  • 4
  • 15
4

Swift Version, if need:

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
2

You can keep a string with the category identifier when scheduling the notification like so

        localNotification.category = NotificationHelper.categoryIdentifier

and search for it and cancel when needed like so

let  app = UIApplication.sharedApplication()

    for notification in app.scheduledLocalNotifications! {
        if let cat = notification.category{
            if cat==NotificationHelper.categoryIdentifier {
                app.cancelLocalNotification(notification)
                break
            }

        }
    }
pantos27
  • 629
  • 4
  • 13
2

swift 3-style:

final private func cancelLocalNotificationsIfIOS9(){


//UIApplication.shared.cancelAllLocalNotifications()
let app = UIApplication.shared
guard let notifs = app.scheduledLocalNotifications else{
    return
}

for oneEvent in notifs {
    let notification = oneEvent as UILocalNotification
    if let userInfoCurrent = notification.userInfo as? [String:AnyObject], let uid = userInfoCurrent["uid"] as? String{
        if uid == uidtodelete {
            //Cancelling local notification
            app.cancelLocalNotification(notification)
            break;
        }
    }
}

}

for iOS 10 use:

    let center = UNUserNotificationCenter.current()
    center.removePendingNotificationRequests(withIdentifiers: [uidtodelete])
Dheeraj D
  • 4,386
  • 4
  • 20
  • 34
ingconti
  • 10,876
  • 3
  • 61
  • 48
1

The UILocalNotification object you pass to cancelLocalNotification: will match any existing UILocalNotification object with matching properties.

So:

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = @"foo";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];

will present a local notification that can later be cancelled with:

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = @"foo";
[[UIApplication sharedApplication] cancelLocalNotification:notification];
jhibberd
  • 7,466
  • 1
  • 16
  • 9
  • 1
    Thanks. I think you are creating a new notification and then cancelling it. It won't have any effect on my previously scheduled notification and it will still get fired. – Yogi Mar 18 '14 at 10:50
  • Is there any property that can be matching property except alertBody? – Shamsiddin Saidov Jul 25 '16 at 19:23
1

I use this function in Swift 2.0:

  static func DeleteNotificationByUUID(uidToDelete: String) -> Bool {
    let app:UIApplication = UIApplication.sharedApplication()
    // loop on all the current schedualed notifications
    for schedualedNotif in app.scheduledLocalNotifications! {
      let notification = schedualedNotif as UILocalNotification
      let urrentUi = notification.userInfo! as! [String:AnyObject]
      let currentUid = urrentUi["uid"]! as! String
      if currentUid == uidToDelete {
        app.cancelLocalNotification(notification)
        return true
      }
    }
    return false
  }

Inspired from @KingofBliss's Answer

MBH
  • 16,271
  • 19
  • 99
  • 149
0

For Repeated Reminders ( For example you want your alarm to fire on Sun, Sat and Wed at 4 PM , Then you have to make 3 alarms and set repeatInterval to NSWeekCalendarUnit ).

For making Once Only Reminder :

UILocalNotification *aNotification = [[UILocalNotification alloc] init];
                aNotification.timeZone = [NSTimeZone defaultTimeZone];
                aNotification.alertBody = _reminderTitle.text;
                aNotification.alertAction = @"Show me!";
                aNotification.soundName = UILocalNotificationDefaultSoundName;
                aNotification.applicationIconBadgeNumber += 1;

                NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
                NSDateComponents *componentsForFireDate = [calendar components:(NSYearCalendarUnit | NSWeekCalendarUnit|  NSHourCalendarUnit | NSMinuteCalendarUnit| NSSecondCalendarUnit | NSWeekdayCalendarUnit) fromDate: _reminderDate];

                [componentsForFireDate setHour: [componentsForFireDate hour]] ; //for fixing 8PM hour
                [componentsForFireDate setMinute:[componentsForFireDate minute]];

                [componentsForFireDate setSecond:0] ;
                NSDate *fireDateOfNotification = [calendar dateFromComponents: componentsForFireDate];
                aNotification.fireDate = fireDateOfNotification;
                NSDictionary *infoDict = [NSDictionary dictionaryWithObject:_reminderTitle.text forKey:kRemindMeNotificationDataKey];
                aNotification.userInfo = infoDict;

                [[UIApplication sharedApplication] scheduleLocalNotification:aNotification];

For Making Repeated Reminder :

for (int i = 0 ; i <reminderDaysArr.count; i++)
                {

                    UILocalNotification *aNotification = [[UILocalNotification alloc] init];
                    aNotification.timeZone = [NSTimeZone defaultTimeZone];
                    aNotification.alertBody = _reminderTitle.text;
                    aNotification.alertAction = @"Show me!";
                    aNotification.soundName = UILocalNotificationDefaultSoundName;
                    aNotification.applicationIconBadgeNumber += 1;

                    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
                    NSDateComponents *componentsForFireDate = [calendar components:(NSYearCalendarUnit | NSWeekCalendarUnit|  NSHourCalendarUnit | NSMinuteCalendarUnit| NSSecondCalendarUnit | NSWeekdayCalendarUnit) fromDate: _reminderDate];


                    [componentsForFireDate setWeekday: [[reminderDaysArr objectAtIndex:i]integerValue]];

                    [componentsForFireDate setHour: [componentsForFireDate hour]] ; // Setup Your Own Time.
                    [componentsForFireDate setMinute:[componentsForFireDate minute]];

                    [componentsForFireDate setSecond:0] ;
                    NSDate *fireDateOfNotification = [calendar dateFromComponents: componentsForFireDate];
                    aNotification.fireDate = fireDateOfNotification;
                    aNotification.repeatInterval = NSWeekCalendarUnit;
                    NSDictionary *infoDict = [NSDictionary dictionaryWithObject:_reminderTitle.text forKey:kRemindMeNotificationDataKey];
                    aNotification.userInfo = infoDict;

                    [[UIApplication sharedApplication] scheduleLocalNotification:aNotification];
                }
            }

For Filtering you array to display it.

-(void)filterNotficationsArray:(NSMutableArray*) notificationArray{

    _dataArray = [[NSMutableArray alloc]initWithArray:[[UIApplication sharedApplication] scheduledLocalNotifications]];
    NSMutableArray *uniqueArray = [NSMutableArray array];
    NSMutableSet *names = [NSMutableSet set];

    for (int i = 0 ; i<_dataArray.count; i++) {
        UILocalNotification *localNotification = [_dataArray objectAtIndex:i];
        NSString * infoDict = [localNotification.userInfo objectForKey:@"kRemindMeNotificationDataKey"];

        if (![names containsObject:infoDict]) {
            [uniqueArray addObject:localNotification];
            [names addObject:infoDict];
        }
    }
    _dataArray = uniqueArray;
}

To remove Reminder even it was Once Only or Repeated :

- (void) removereminder:(UILocalNotification*)notification
{
    _dataArray = [[NSMutableArray alloc]initWithArray:[[UIApplication sharedApplication]scheduledLocalNotifications]];

    NSString * idToDelete = [notification.userInfo objectForKey:@"kRemindMeNotificationDataKey"];
    for (int i = 0 ; i<_dataArray.count; i++)
    {
        UILocalNotification *currentLocalNotification = [_dataArray objectAtIndex:i];
        NSString * notificationId = [currentLocalNotification.userInfo objectForKey:@"kRemindMeNotificationDataKey"];

        if ([notificationId isEqualToString:idToDelete])
            [[UIApplication sharedApplication]cancelLocalNotification:currentLocalNotification];
    }

    _dataArray = [[NSMutableArray alloc]initWithArray:[[UIApplication sharedApplication]scheduledLocalNotifications]];
    [self filterNotficationsArray:_dataArray];
    [_remindersTV reloadData];

}
Atef
  • 2,872
  • 1
  • 36
  • 32
0

I expanded on KingofBliss's answer a little, written this a little more Swift2-like, removed some unnecessary code, and added in some crash guards.

To start, when creating the notification, you need to make sure you set the uid (or any custom property really) of the notification's userInfo:

notification.userInfo = ["uid": uniqueid]

Then, when deleting it, you can do:

guard
    let app: UIApplication = UIApplication.sharedApplication(),
    let notifications = app.scheduledLocalNotifications else { return }
for notification in notifications {
    if
        let userInfo = notification.userInfo,
        let uid: String = userInfo["uid"] as? String where uid == uidtodelete {
            app.cancelLocalNotification(notification)
            print("Deleted local notification for '\(uidtodelete)'")
    }
}
brandonscript
  • 68,675
  • 32
  • 163
  • 220
  • 1
    For safety you could use the guard-statement guard let app = UIApplication.sharedApplication() else { return false } for schedualedNotif in app.scheduledLocalNotifications { ... } Then you don't need to force unwrap it in the for-loop – troligtvis Mar 30 '16 at 12:39
0

Delete already delivered notification Swift5

       UNUserNotificationCenter.current().getDeliveredNotifications{ (requests) in
            for request in requests {
                if request.request.identifier == "identifier"{
                    UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["identifier"])
                }
            }
        }
Wahab Khan Jadon
  • 875
  • 13
  • 21