115

I have set up local notifications in the App Delegate Using this:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    UILocalNotification *notification = [[UILocalNotification alloc]init];
    [notification setAlertBody:@"Watch the Latest Episode of CCA-TV"];
    [notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:5]];
    [notification setTimeZone:[NSTimeZone defaultTimeZone]];
    [application setScheduledLocalNotifications:[NSArray arrayWithObject:notification]];
}

When I run the app and then quit it I receive an error saying:

2014-06-07 11:14:16.663 CCA-TV[735:149070] Attempting to schedule a local notification {fire date = Saturday, June 7, 2014 at 11:14:21 Pacific Daylight Time, time zone = America/Los_Angeles (PDT) offset -25200 (Daylight), repeat interval = 0, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Saturday, June 7, 2014 at 11:14:21 Pacific Daylight Time, user info = (null)} with an alert but haven't received permission from the user to display alerts

How can I get the necessary permission to display the alerts?

JOM
  • 8,139
  • 6
  • 78
  • 111
dannysandler
  • 1,881
  • 4
  • 16
  • 14
  • 1
    I think the app has rejected the permission once, you can try enable from Settings. But by the way UILocalNotification don't need user permission.. – iphonic Jun 07 '14 at 18:39
  • Try `registerUserNotificationSettings`. Had it been iOS 8, this thread would have answered your question. But, g ahead have a look -http://stackoverflow.com/questions/24006998/implicit-conversion-of-unsigned-long-uiusernotificationsettings-is-disallow – raurora Jun 07 '14 at 20:43

5 Answers5

236

Since iOS 8 you need to ask user's permission to show notifications from your app, this applies for both remote/push and local notifications. In Swift you can do it like this,

Update for Swift 2.0

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    // Override point for customization after application launch.
    if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:")))
    {
        let notificationCategory:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
        notificationCategory.identifier = "INVITE_CATEGORY"
        notificationCategory.setActions([replyAction], forContext: UIUserNotificationActionContext.Default)

        //registerting for the notification.
        application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes:[.Sound, .Alert, .Badge], categories: nil))
    }
    else
    {
       //do iOS 7 stuff, which is pretty much nothing for local notifications.
    }
    return true
}

Swift 3.2

if(UIApplication.instancesRespond(to: #selector(UIApplication.registerUserNotificationSettings(_:)))){
     let notificationCategory:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
     notificationCategory.identifier = "INVITE_CATEGORY"
     notificationCategory.setActions([replyAction], forContext: UIUserNotificationActionContext.Default)

     //registerting for the notification.
        application.registerUserNotificationSettings(UIUserNotificationSettings(types:[.sound, .alert, .badge], categories: nil))
}
else{
        //do iOS 7 stuff, which is pretty much nothing for local notifications.
    }

Objective C syntax is also very similar.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){
        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
    }
    // Override point for customization after application launch.
    return YES;
}

To check for currently registered notification types you can use UIApplication class's method,

- (UIUserNotificationSettings *)currentUserNotificationSettings

So if the user has said no to your app then this function should return a setting without any types in it.

I have written a tutorial about this, you could see it here.

Satheesh
  • 10,998
  • 6
  • 50
  • 93
  • 1
    If the user denies permission, how do you determine this later programatically? – jjxtra Jun 18 '14 at 17:50
  • @satheeshwaran When I use this code, It is working fine with simulator with iOS8. I wanted my app's deployment target starting from iOS7. So, when I run this code on an iOS7 device, I get this error: `dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings`. Is there any other way in Swift to ask user for permissions in order to work in iOS7? please help. – Raghavendra Aug 23 '14 at 11:33
  • @Raghav UIUserNotificationSettings is only available from iOS 8 and what you are facing is the right behaviour. You shouldn't use this in iOS 7. – Satheesh Aug 23 '14 at 17:39
  • @satheeshwaran Is there any way we can ask user for permission in swift in iOS7, because I started my project in Swift. I need to achieve this in Swift. Please help. Thanks! – Raghavendra Aug 25 '14 at 05:26
  • @Raghav Dude, the registerUserNotificationSettings call actually shows the user a alert asking for the permission if it is the first time. See my tutorial for more details. – Satheesh Aug 25 '14 at 05:41
  • @satheeshwaran Yes, I got the alert asking for permission first time if I run it on simulator running iOS8, but I am testing on iPad2 with iOS7, So I got error with UIUserNotificationSettings because it is available only on iOS8, now how do I call registerUserNotificationSettings to be compatible with iOS7? – Raghavendra Aug 25 '14 at 05:48
  • @satheeshwaran do we need not ask for any permission in order to send local notifications in iOS7? – Raghavendra Aug 25 '14 at 05:57
  • @Raghav No for iOS 7 you don't need to ask for user permission for sending local notifications, this has changed only in iOS 8. – Satheesh Aug 25 '14 at 05:59
  • @satheeshwaran Now I got some clarity over this topic. I have been breaking my head since yesterday. Thanks dude. You are the savior! – Raghavendra Aug 25 '14 at 06:00
  • @Raghav Man you can check whether that class is available before dealing with that permission statement. Wait for two mins I will update the syntax for that too. – Satheesh Aug 25 '14 at 06:01
  • @Raghav See my edit to support iOS 7 and 8, but in iOS 7 it is crashing with this message dyld: Symbol not found: _OBJC_CLASS_$_UIMutableUserNotificationAction, I think a bug in Xcode 6 beta. – Satheesh Aug 25 '14 at 06:47
  • @satheeshwaran Instead of checking for OS version, checking for the class if available will be better I think `if let iOS8: AnyClass = NSClassFromString("UIAlertController") { ... }` – Raghavendra Aug 25 '14 at 09:04
  • @Raghav Yes you could do that as well, I tried doing it but thought this would be simpler. – Satheesh Sep 08 '14 at 06:15
  • 1
    -1 for checking iOS version UIDevice. http://stackoverflow.com/a/25735175/1226304 answer has better approach to do this. – derpoliuk Sep 18 '14 at 08:30
  • 3
    @derpoliuk Updated as in the answer for everyone's benefit, ok?? – Satheesh Sep 18 '14 at 15:53
  • @derpoliuk Sorry man some one rejected your edit, I have updated the answer according to your edit, Thanks! – Satheesh Sep 19 '14 at 10:28
  • Is there a way to see determine if the user has not been asked for permission yet in iOS 8? [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] returns NO in both cases. – reallyseth Sep 19 '14 at 21:28
  • check this method currentUserNotificationSettings. – Satheesh Sep 21 '14 at 06:45
  • If you are going to replace your original answer with other people's answers, at least please make it a community wiki. Thank you. – KPM Oct 03 '14 at 08:25
  • Check out this https://github.com/alexruperez/UIUserNotificationSettings-Extension – alexruperez Nov 21 '14 at 00:35
  • hey @satheeshwaran thanks for this, but I wonder; is there a way to precede this iOS confirm dialog with a dialog of my own that explains why it's helpful to allow notifications with my app when the user taps a button that will use notifications? I put this code in an IBAction and I get an 'Use of unresolved identifier 'application'' error in xcode. – jammyman34 Dec 31 '14 at 17:48
  • @jammyman34 No man you can't replace the default dialog with a dialog of your own but one thing you can do and I have seen in couple of apps is, showing your custom dialog describing that" in the next step you will be asked for some permission blah blah"..and then when the user says Ok you call the registerNotificationsSettings method, which shows the default alert. – Satheesh Jan 02 '15 at 05:38
  • @satheeshwaran I must have asked my question poorly, what you described is what I was 'trying' to ask :) Thanks! – jammyman34 Jan 05 '15 at 22:26
  • @satheeshwaran sorry to bug you again. Just created my pre-dialog that explains why allowing notifs is good for my app. Put the "application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge, categories: nil))" in the handler func for my dialogs OK button, but xcode is giving me an error "Compiler Error: Use of unresolved identifier 'application'" Thoughts on what I'm doing wrong? – jammyman34 Jan 07 '15 at 21:14
  • @jammyman34 man application variable is not defined in the place where you are trying to work with it so instead of application use UIApplication.sharedApplication object. – Satheesh Jan 08 '15 at 05:55
  • @satheeshwaran thanks, that did indeed get rid of the compile error.Now I have to figure out how to get it to check if the user has already seen the registration dialog conditionally so it doesn't show my special dialog every single time I hit the button, which it's doing now. I'm just a UX designer trying to program btw, so forgive me for my noob-ness. – jammyman34 Jan 08 '15 at 22:40
  • @jammyman34 Why should I forgive you, you have taken the time to come here and ask questions many don't even do that. Hmmm coming to your question you could check whether the user is registered for push notifications already if so show your alert then register for push everytime. If the user has already registered for push then don't your alert. Use this function to check for the same in iOS 8, isRegisteredForRemoteNotifications. – Satheesh Jan 09 '15 at 08:39
  • See this http://stackoverflow.com/questions/25570015/ios8-check-permission-of-remotenotificationtype for more info. – Satheesh Jan 09 '15 at 08:40
  • @satheeshwaran thanks dude, I'll try to figure out to convert the other threads func into swift. – jammyman34 Jan 10 '15 at 01:02
  • @satheeshwaran alright, I give up. I can't figure out how to do the isRegisteredForremoteNotifications in swift :( – jammyman34 Jan 17 '15 at 00:40
  • @jammyman34 Try if(UIApplication.sharedApplication().isRegisteredForRemoteNotifications()){//do something} else{//do something else} – Satheesh Jan 17 '15 at 10:54
  • Is it just me or is someone taking credit? https://www.topcoder.com/blog/notifications-in-ios-8-using-swift/ Great tut btw! – The Dude Aug 02 '15 at 19:35
  • @user2927356 Thanks, but I did not get the taking credit part what do you mean by it. – Satheesh Aug 13 '15 at 02:44
  • since you are using Swift 2.0, might as well make use of the Availability APIs – Mazyod Oct 11 '15 at 19:54
  • @Mazyod Yes sure will do that, thanks for reminding me – Satheesh Oct 12 '15 at 03:07
38

Put this code in the view controller where you will first program the notifications (if you program them at launch, then it will be application:didFinishLaunchingWithOptions:):

if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound categories:nil]];
}

In Swift:

if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:"))) {
    UIApplication.sharedApplication().registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert | .Sound, categories: nil))
}

The solutions that test against system version number are sub-optimal and error-prone.

KPM
  • 10,558
  • 3
  • 45
  • 66
  • I would use `application.respondsToSelector(Selector("registerUserNotificationSettings"))` and `if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])` – derpoliuk Sep 19 '14 at 12:49
  • 7
    Well that's only because you're using it inside `application:didFinishLaunchingWithOptions:` which provides a handy `application` object :) – KPM Sep 19 '14 at 12:53
18

Try this for Objective-C:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:    (NSDictionary *)launchOptions
{
// are you running on iOS8?
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) 
  {
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound) categories:nil];
    [application registerUserNotificationSettings:settings];
  } 
else // iOS 7 or earlier
  {
    UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
    [application registerForRemoteNotificationTypes:myTypes];
  }
}

For Swift:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
// Override point for customization after application launch.
 if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:")))
 {
    application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge, categories: nil))
 }
 else
 {
    //
 }
return true
}
Nagarjun
  • 6,557
  • 5
  • 33
  • 51
5

I just faced the same problem. Seems like in iOS 8 we need to do an additional step, usually done inside:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /*...*/ }

You can use this code if you want to keep it backward compatible:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)])
    {
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil]];
    }
#endif

The system will remember the decision, and will only ask once.

ppalancica
  • 4,236
  • 4
  • 27
  • 42
1

**Local Notification with three button action for iOS8+

//Button : I TOOK IT , REMIND LATER , SKIP IT**

        let completeAction = UIMutableUserNotificationAction()
        completeAction.identifier = "COMPLETE_TODO"
        completeAction.title = "I TOOK IT"
        completeAction.activationMode = .Background
        completeAction.destructive = true
        completeAction.authenticationRequired = false

        let remindAction = UIMutableUserNotificationAction()
        remindAction.identifier = "REMIND_TODO"
        remindAction.title = "REMIND LATER"
        remindAction.activationMode = .Background
        remindAction.destructive = false
        //  remindAction.authenticationRequired = false

        let skipAction = UIMutableUserNotificationAction()
        skipAction.identifier = "SKIP_TODO"
        skipAction.title = "SKIP IT"
        skipAction.activationMode = .Background
        skipAction.destructive = false
        skipAction.authenticationRequired = false


        let todoCategory = UIMutableUserNotificationCategory()
        todoCategory.identifier = "TODO_CATEGORY"
        todoCategory.setActions([completeAction, remindAction, skipAction], forContext: .Default)
        todoCategory.setActions([completeAction,remindAction,skipAction], forContext: .Minimal)


        if application.respondsToSelector("isRegisteredForRemoteNotifications")
        {

            let categories = NSSet(array: [todoCategory,todoVideoCategory])
            let types:UIUserNotificationType = ([.Alert, .Sound, .Badge])

            let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: categories as? Set<UIUserNotificationCategory>)

            application.registerUserNotificationSettings(settings)
            application.registerForRemoteNotifications()

        }

    }
PSS
  • 316
  • 1
  • 9