iOS7 and iOS8: how to detect when user said No to a request for push notifications
Asked Answered
R

3

17

I am building an app that targets iOS7 and iOS8. I ask the user for permission to send push notifications. But for some reason, neither iOS7 nor iOS8 ever calls my application:didFailToRegisterForRemoteNotificationsWithError handler if I click "No" when asked for push permission.

My question: how do I know, on both iOS7 and iOS8, when the user has dismissed the request for push notifications--and how do I know if they denied the request?

I've looked at a bunch of StackOverflow answers and have implemented what they suggest, but it's not working as documented:

  • iOS7: if the user approves the request, the system does call my application:didRegisterForRemoteNotificationsWithDeviceToken: handler. So I can tell that they approved it. But if the user denies the request then I don't get a callback to application:didFailToRegisterForRemoteNotificationsWithError. This seems like a bug, and I can't tell that the user denied the request.
  • iOS8: if the user approves or denies the request, the system does call my application:didRegisterUserNotificationSettings handler. I can look at the notificationSettings parameter to see if the user approved or denied my request, so that's handy. However, if I subsequently call isRegisteredForRemoteNotifications() (e.g., when the app becomes active later on), it always returns true--even if the user had denied the request. So I get a false positive. This seems like a bug and I see that others have noticed this as well. Update: if I subsequently call let settings = UIApplication.sharedApplication().currentUserNotificationSettings(), I can check settings.types to see if alerts are allowed. So for iOS8, it looks like I'm all set.

I'm using an NSUserDefaults boolean to keep track of whether I've already asked the user to grant permission.

I am using hardware devices for testing (iPhone 4S with iOS7 and iPhone 5 with iOS8), not a simulator.

I am resetting my device after each test, to make it show the request alert again.

Here's how I register for push notifications. The if branch is taken on iOS8 and the else branch is taken on iOS7:

    let application = UIApplication.sharedApplication()

    if (application.respondsToSelector("registerUserNotificationSettings:")) {
        let settings = UIUserNotificationSettings(forTypes: .Badge | .Alert | .Sound,
            categories: nil )
        application.registerUserNotificationSettings(settings)
    } else {
        application.registerForRemoteNotificationTypes(.Badge | .Alert | .Sound)
    }

(In iOS8, when application:didRegisterUserNotificationSettings: is called, I then call application.registerForRemoteNotifications()).

Reprovable answered 27/11, 2014 at 1:23 Comment(0)
P
6

Your didFailToRegisterForRemoteNotificationsWithError method isn't called, because the registration didn't fail - it was never even attempted because the user denied the request.

On iOS7 you can do a couple of things:

  1. Assume that remote notifications aren't available until didRegisterForRemoteNotificationsWithDeviceToken is called
  2. Check the value enabledRemoteNotificationTypes on the UIApplication object
Peart answered 27/11, 2014 at 3:55 Comment(5)
Thanks...so in other words, there's no way for me to be notified that the user has denied the app's request for pushes. I was originally planning to have an onboarding page that explains the benefit of push notifications, with a button at the bottom that says "Turn on push notifications." After they click it and approve or deny, I was going to replace the button with a message based on whether they approved or denied.Reprovable
No, you don't get notified explicitly that they have denied. You can only attempt to infer it from a lack of notification that they have approvedPeart
not sure, but looks like works no longer, "enabledRemoteNotificationTypes is not supported in iOS 8.0 and later."Glossotomy
Now you can check isRegisteredForRemoteNotifications on the UIApplication objectPeart
Same need here using Swift and iOS8. Looking into UIUserNotificationSettings types supported are .Alert .Sound. .Badge and .None. The issue is that .None will be returned both if it's never been checked and if the user denies. It seems that we'll need to add our own logic to differentiate this condition further and respond accordingly.Poore
H
7

I was trying to find a way around this same issue.

When the push notification permission UIAlert is shown, it is shown from outside of my app. Once the user has selected "Don't Allow" or "OK", my app is becoming active again.

I have a view controller that I'm presenting before prompting the user for Push permissions. In this view controller I listen for the UIApplicationDidBecomeActiveNotification, and then dismiss my view controller.

This has been working pretty well, whereas every other solution I've seen hasn't worked for me at all.

Halogenate answered 12/4, 2015 at 0:41 Comment(1)
As Paulw11 mentions in his answer and comments you can check isRegisteredForRemoteNotifications on UIApplication to see if they have accepted or notHalogenate
P
6

Your didFailToRegisterForRemoteNotificationsWithError method isn't called, because the registration didn't fail - it was never even attempted because the user denied the request.

On iOS7 you can do a couple of things:

  1. Assume that remote notifications aren't available until didRegisterForRemoteNotificationsWithDeviceToken is called
  2. Check the value enabledRemoteNotificationTypes on the UIApplication object
Peart answered 27/11, 2014 at 3:55 Comment(5)
Thanks...so in other words, there's no way for me to be notified that the user has denied the app's request for pushes. I was originally planning to have an onboarding page that explains the benefit of push notifications, with a button at the bottom that says "Turn on push notifications." After they click it and approve or deny, I was going to replace the button with a message based on whether they approved or denied.Reprovable
No, you don't get notified explicitly that they have denied. You can only attempt to infer it from a lack of notification that they have approvedPeart
not sure, but looks like works no longer, "enabledRemoteNotificationTypes is not supported in iOS 8.0 and later."Glossotomy
Now you can check isRegisteredForRemoteNotifications on the UIApplication objectPeart
Same need here using Swift and iOS8. Looking into UIUserNotificationSettings types supported are .Alert .Sound. .Badge and .None. The issue is that .None will be returned both if it's never been checked and if the user denies. It seems that we'll need to add our own logic to differentiate this condition further and respond accordingly.Poore
B
2

Assuming that you're compiling against iOS8 or newer, you can use the following delegate method:

func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
    print("Notifications status: \(notificationSettings)")
}

then on your didFinishLaunching (or where is appropriate for your needs) you must invoke:

let app = UIApplication.sharedApplication()
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
app.registerUserNotificationSettings(settings)
app.registerForRemoteNotifications()

At this point, your users will be prompted with a typical Allow/Don't Allow prompt message. Regardless of your user's choice you will get a call to the method above, allowing for a fine grain configuration of your app. The result will be something like:

Notification Status: <UIUserNotificationSettings: 0x7fa261701da0; types: (none);>

Notification Status: <UIUserNotificationSettings: 0x7ff032e21dd0; types: (UIUserNotificationTypeAlert UIUserNotificationTypeBadge UIUserNotificationTypeSound);>

In the first case, you will notice that the user denied the notifications. The second one is about the allow option.

Biggin answered 20/6, 2016 at 20:4 Comment(2)
Why do you think didRegisterUserNotificationSettings will be called if user have denied?Enslave
I tested this on both a simulator and an iPhone 6s in iOS 9. They both worked as described. Thanks for the answer!Nowt

© 2022 - 2024 — McMap. All rights reserved.