Ask for User Permission to Receive UILocalNotifications in iOS 8

64,617

Solution 1

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.

Solution 2

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.

Solution 3

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
}

Solution 4

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.

Solution 5

**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()

        }

    }
Share:
64,617

Related videos on Youtube

dannysandler
Author by

dannysandler

Updated on June 13, 2020

Comments

  • dannysandler
    dannysandler about 4 years

    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?

    • iphonic
      iphonic about 10 years
      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..
    • raurora
      raurora about 10 years
      Try registerUserNotificationSettings. Had it been iOS 8, this thread would have answered your question. But, g ahead have a look -stackoverflow.com/questions/24006998/…
  • jjxtra
    jjxtra about 10 years
    If the user denies permission, how do you determine this later programatically?
  • Raghavendra
    Raghavendra almost 10 years
    @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.
  • Satheesh
    Satheesh almost 10 years
    @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.
  • Raghavendra
    Raghavendra almost 10 years
    @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!
  • Satheesh
    Satheesh almost 10 years
    @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.
  • Raghavendra
    Raghavendra almost 10 years
    @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
    Raghavendra almost 10 years
    @satheeshwaran do we need not ask for any permission in order to send local notifications in iOS7?
  • Satheesh
    Satheesh almost 10 years
    @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.
  • Raghavendra
    Raghavendra almost 10 years
    @satheeshwaran Now I got some clarity over this topic. I have been breaking my head since yesterday. Thanks dude. You are the savior!
  • Satheesh
    Satheesh almost 10 years
    @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
    Satheesh almost 10 years
    @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.
  • Raghavendra
    Raghavendra almost 10 years
    @satheeshwaran Instead of checking for OS version, checking for the class if available will be better I think if let iOS8: AnyClass = NSClassFromString("UIAlertController") { ... }
  • Satheesh
    Satheesh almost 10 years
    @Raghav Yes you could do that as well, I tried doing it but thought this would be simpler.
  • derpoliuk
    derpoliuk almost 10 years
    -1 for checking iOS version UIDevice. stackoverflow.com/a/25735175/1226304 answer has better approach to do this.
  • Satheesh
    Satheesh almost 10 years
    @derpoliuk Updated as in the answer for everyone's benefit, ok??
  • Satheesh
    Satheesh almost 10 years
    @derpoliuk Sorry man some one rejected your edit, I have updated the answer according to your edit, Thanks!
  • derpoliuk
    derpoliuk almost 10 years
    I would use application.respondsToSelector(Selector("registerUserNotific‌​ationSettings")) and if ([application respondsToSelector:@selector(registerUserNotificationSetting‌​s:)])
  • KPM
    KPM almost 10 years
    Well that's only because you're using it inside application:didFinishLaunchingWithOptions: which provides a handy application object :)
  • reallyseth
    reallyseth almost 10 years
    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.
  • Satheesh
    Satheesh almost 10 years
    check this method currentUserNotificationSettings.
  • KPM
    KPM over 9 years
    If you are going to replace your original answer with other people's answers, at least please make it a community wiki. Thank you.
  • alexruperez
    alexruperez over 9 years
  • jammyman34
    jammyman34 over 9 years
    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.
  • Satheesh
    Satheesh over 9 years
    @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.
  • jammyman34
    jammyman34 over 9 years
    @satheeshwaran I must have asked my question poorly, what you described is what I was 'trying' to ask :) Thanks!
  • jammyman34
    jammyman34 over 9 years
    @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(UIUserNotifica‌​tionSettings(forType‌​s: 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?
  • Satheesh
    Satheesh over 9 years
    @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.
  • jammyman34
    jammyman34 over 9 years
    @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.
  • Satheesh
    Satheesh over 9 years
    @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
    Satheesh over 9 years
  • jammyman34
    jammyman34 over 9 years
    @satheeshwaran thanks dude, I'll try to figure out to convert the other threads func into swift.
  • jammyman34
    jammyman34 over 9 years
    @satheeshwaran alright, I give up. I can't figure out how to do the isRegisteredForremoteNotifications in swift :(
  • Satheesh
    Satheesh over 9 years
    @jammyman34 Try if(UIApplication.sharedApplication().isRegisteredForRemoteNo‌​tifications()){//do something} else{//do something else}
  • The Dude
    The Dude almost 9 years
    Is it just me or is someone taking credit? topcoder.com/blog/notifications-in-ios-8-using-swift Great tut btw!
  • Satheesh
    Satheesh almost 9 years
    @user2927356 Thanks, but I did not get the taking credit part what do you mean by it.
  • Mazyod
    Mazyod over 8 years
    since you are using Swift 2.0, might as well make use of the Availability APIs
  • Satheesh
    Satheesh over 8 years
    @Mazyod Yes sure will do that, thanks for reminding me
  • Mehul Chuahan
    Mehul Chuahan about 7 years
    Better to check iOS version with this code if ([[UIDevice currentDevice].systemVersion floatValue] < 10)