Swift ios check if remote push notifications are enabled in ios9 and ios10
Asked Answered
L

13

96

How can I check if the user has enabled remote notifications on ios 9 or ios 10?

If the user has not allowed or clicked No I want to toggle a message asking if they want to enable notifications.

Laszlo answered 10/11, 2016 at 15:21 Comment(0)
C
77

Updated answer after iOS 10 is using UNUserNotificationCenter .

First you need to import UserNotifications then

let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
    switch permission.authorizationStatus  {
    case .authorized:
        print("User granted permission for notification")
    case .denied:
        print("User denied notification permission")
    case .notDetermined:
        print("Notification permission haven't been asked yet")
    case .provisional:
        // @available(iOS 12.0, *)
        print("The application is authorized to post non-interruptive user notifications.")
    case .ephemeral:
        // @available(iOS 14.0, *)
        print("The application is temporarily authorized to post notifications. Only available to app clips.")
    @unknown default:
        print("Unknow Status")
    }
})

this code will work till iOS 9, for iOS 10 use the above code snippet.

let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
if isRegisteredForRemoteNotifications {
     // User is registered for notification
} else {
     // Show alert user is not registered for notification
}
Cartography answered 10/11, 2016 at 15:32 Comment(8)
This doesn't appear to work for iOS 10. In the simulator I clicked "Don't Allow" and this code still said user is registered for remote notifications.Crosse
Works for me on iOS 10. Try using an actual device instead of the simulator.Anesthesia
It tells you only if the token was ever generated (the device was registered), not if the notifications were blocked.Calvincalvina
Remote Notifications are not supported in the iOS simulator. Only local notificationsSchoolhouse
isRegisteredForRemoteNotifications works agin on iOS 12, not iOS 10-11.Pedicle
This is not the correct way of determining this. When notifications are disabled by the user this property continues to return true even across application restarts. This is odd as it goes against the documentation that states, "The value returned by this method takes into account the user’s preferences for receiving remote notifications." You need to check if notifications are allowed by the user as well.Ventriloquist
I noticed that you did not add break statements in the switch statement. Are these not required?Labradorite
@AbdullahUmer yeah, break statement is not required here. The reason is if a switch case has any statement in it, it will automatically end the execution at the end, it will not go to another case.Cartography
D
169

Apple recommends to use UserNotifications framework instead of shared instances. So, do not forget to import UserNotifications framework. As this framework is new in iOS 10 it's really only safe to use this code in apps building for iOS10+

let current = UNUserNotificationCenter.current()

current.getNotificationSettings(completionHandler: { (settings) in
    if settings.authorizationStatus == .notDetermined {
        // Notification permission has not been asked yet, go for it!
    } else if settings.authorizationStatus == .denied {
        // Notification permission was previously denied, go to settings & privacy to re-enable
    } else if settings.authorizationStatus == .authorized {
        // Notification permission was already granted
    }
})

You may check official documentation for further information: https://developer.apple.com/documentation/usernotifications

Doubling answered 7/6, 2017 at 8:33 Comment(10)
Seems to me this is the right answer as of July 2017.Antefix
why isn't this if if else and if else?Chiropteran
@OgulcanOrhan yeah I know it works - I used your code and upvoted your answer just so you know :) - I just wanted to know why all three conditionals need to be called? I'm being a little pedantic I supposeChiropteran
Yeah, I personally would choose to use a switch statement.Rhoades
Apart from using else if use switch statements for avoiding any confusionBarbbarba
It's amazing how they guys at apple always achieve to make something so simple as accessing two booleans to become a mess of async request. I am really curious to know the reasons behind such choices.Flaggy
This only works with iOS 10+. If you need to support both iOS 9 and iOS 10+ you will need to use a combination of the methods found in this post.Boling
what if notifications was enabled and then switched out in the phone settings?Materialize
@Flaggy but it's not a bool, it's potentially one of many states (currently 5). Also, even if you had a helper that turned it into a bool, it probably wouldn't be too useful as your product would most likely have different behavior depending on if it's .notDetermined or .denied anyways.Uturn
@Uturn it was an hyperbole, I meant more in general why don't they provide a callback for the changed values forcing to create wrappers and handle states internally, why (at least at time this was written) documentation was very lacking, too many cases to handle (especially as of today with approximate location, temporary permission), behaviours changing depending on app state, etc.Flaggy
C
77

Updated answer after iOS 10 is using UNUserNotificationCenter .

First you need to import UserNotifications then

let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
    switch permission.authorizationStatus  {
    case .authorized:
        print("User granted permission for notification")
    case .denied:
        print("User denied notification permission")
    case .notDetermined:
        print("Notification permission haven't been asked yet")
    case .provisional:
        // @available(iOS 12.0, *)
        print("The application is authorized to post non-interruptive user notifications.")
    case .ephemeral:
        // @available(iOS 14.0, *)
        print("The application is temporarily authorized to post notifications. Only available to app clips.")
    @unknown default:
        print("Unknow Status")
    }
})

this code will work till iOS 9, for iOS 10 use the above code snippet.

let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
if isRegisteredForRemoteNotifications {
     // User is registered for notification
} else {
     // Show alert user is not registered for notification
}
Cartography answered 10/11, 2016 at 15:32 Comment(8)
This doesn't appear to work for iOS 10. In the simulator I clicked "Don't Allow" and this code still said user is registered for remote notifications.Crosse
Works for me on iOS 10. Try using an actual device instead of the simulator.Anesthesia
It tells you only if the token was ever generated (the device was registered), not if the notifications were blocked.Calvincalvina
Remote Notifications are not supported in the iOS simulator. Only local notificationsSchoolhouse
isRegisteredForRemoteNotifications works agin on iOS 12, not iOS 10-11.Pedicle
This is not the correct way of determining this. When notifications are disabled by the user this property continues to return true even across application restarts. This is odd as it goes against the documentation that states, "The value returned by this method takes into account the user’s preferences for receiving remote notifications." You need to check if notifications are allowed by the user as well.Ventriloquist
I noticed that you did not add break statements in the switch statement. Are these not required?Labradorite
@AbdullahUmer yeah, break statement is not required here. The reason is if a switch case has any statement in it, it will automatically end the execution at the end, it will not go to another case.Cartography
C
40

I tried Rajat's solution, but it didn't work for me on iOS 10 (Swift 3). It always said that push notifications were enabled. Below is how I solved the problem. This says "not enabled" if the user has tapped "Don't Allow" or if you have not asked the user yet.

let notificationType = UIApplication.shared.currentUserNotificationSettings!.types
    if notificationType == [] {
        print("notifications are NOT enabled")
    } else {
        print("notifications are enabled")
    }

PS: The method currentUserNotificationSettings was deprecated in iOS 10.0 but it's still working.

Crosse answered 28/1, 2017 at 15:44 Comment(8)
Will this work on iOS 9,8,7,etc...or do I need separate code?Sweat
I'm not sure, I've only checked it on iOS 10.Crosse
Cam, I just tested this code on 10.2 (on a phone) and on 9.3 (on the simulator) and it worked on both. tylerSF, thanks for the solution.Hobgoblin
This solution is better since it also managed the case where user goes in settings, able / disable notifications and goes back in applicationCoven
Thanks , what about if i need to change the status for notification from the application , could you please help me in this situation?Brave
Not sure what you mean @wod... what are you trying to change?Crosse
@Brave its not possible yet.Merrymaking
'currentUserNotificationSettings' was deprecated in iOS 10.0: Use UserNotifications Framework's -[UNUserNotificationCenter getNotificationSettingsWithCompletionHandler:] and -[UNUserNotificationCenter getNotificationCategoriesWithCompletionHandler:]Bernardabernardi
H
33

If your app supports iOS 10 and iOS 8, 9 use below code

// At the top, import UserNotifications 
// to use UNUserNotificationCenter
import UserNotifications

Then,

if #available(iOS 10.0, *) {
    let current = UNUserNotificationCenter.current()
    current.getNotificationSettings(completionHandler: { settings in

        switch settings.authorizationStatus {

        case .notDetermined:
            // Authorization request has not been made yet
        case .denied:
            // User has denied authorization.
            // You could tell them to change this in Settings
        case .authorized:
            // User has given authorization.
        }
    })
 } else {
     // Fallback on earlier versions
     if UIApplication.shared.isRegisteredForRemoteNotifications {
         print("APNS-YES")
     } else {
         print("APNS-NO")
     }
 }
Hyetal answered 11/7, 2017 at 5:46 Comment(0)
W
21

in iOS11, Swift 4...

 UNUserNotificationCenter.current().getNotificationSettings { (settings) in
        if settings.authorizationStatus == .authorized {
            // Already authorized
        }
        else {
            // Either denied or notDetermined
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
                (granted, error) in
                  // add your own 
                UNUserNotificationCenter.current().delegate = self
                let alertController = UIAlertController(title: "Notification Alert", message: "please enable notifications", preferredStyle: .alert)
                let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
                    guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
                        return
                    }
                    if UIApplication.shared.canOpenURL(settingsUrl) {
                        UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
                        })
                    }
                }
                let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
                alertController.addAction(cancelAction)
                alertController.addAction(settingsAction)
                DispatchQueue.main.async {
                    self.window?.rootViewController?.present(alertController, animated: true, completion: nil)

                }
            }
        }
    }
Woolard answered 9/11, 2017 at 5:34 Comment(2)
I'm getting an error with this Use of unresolved identifier 'UNUserNotificationCenter'; did you mean 'NSNotificationCenter'?Tension
@Tension you have to import UserNotificationsQuinquereme
F
10

@Rajat's answer is not enough.

  • isRegisteredForRemoteNotifications is that your app has connected to APNS and get device token, this can be for silent push notification
  • currentUserNotificationSettings is for user permissions, without this, there is no alert, banner or sound push notification delivered to the app

Here is the check

static var isPushNotificationEnabled: Bool {
  guard let settings = UIApplication.shared.currentUserNotificationSettings
    else {
      return false
  }

  return UIApplication.shared.isRegisteredForRemoteNotifications
    && !settings.types.isEmpty
}

For iOS 10, instead of checking for currentUserNotificationSettings, you should use UserNotifications framework

center.getNotificationSettings(completionHandler: { settings in
  switch settings.authorizationStatus {
  case .authorized, .provisional:
    print("authorized")
  case .denied:
    print("denied")
  case .notDetermined:
    print("not determined, ask user for permission now")
  }
})

Push notification can be delivered to our apps in many ways, and we can ask for that

UNUserNotificationCenter.current()
  .requestAuthorization(options: [.alert, .sound, .badge])

User can go to Settings app and turn off any of those at any time, so it's best to check for that in the settings object

open class UNNotificationSettings : NSObject, NSCopying, NSSecureCoding {


    open var authorizationStatus: UNAuthorizationStatus { get }


    open var soundSetting: UNNotificationSetting { get }

    open var badgeSetting: UNNotificationSetting { get }

    open var alertSetting: UNNotificationSetting { get }


    open var notificationCenterSetting: UNNotificationSetting { get }
}
Fury answered 7/6, 2017 at 8:41 Comment(1)
Notice that this is deprecated starting iOS 10Exorable
G
8

for iOS12 and Swift 4 also support iOS13 and Swift5 I also created a git for this you can check here

just add this singleton file in your XCode Project

import Foundation
import UserNotifications
import UIKit

class NotificaionStatusCheck {
    
    
    var window: UIWindow?
    
    private var currentViewController : UIViewController? = nil
    
    
     static let shared = NotificaionStatusCheck()
    
    public func currentViewController(_ vc: UIViewController?) {
        self.currentViewController = vc
        checkNotificationsAuthorizationStatus()
    }
    
    
    private func checkNotificationsAuthorizationStatus() {
        let userNotificationCenter = UNUserNotificationCenter.current()
        userNotificationCenter.getNotificationSettings { (notificationSettings) in
            switch notificationSettings.authorizationStatus {
            case .authorized:
                print("The app is authorized to schedule or receive notifications.")
                
            case .denied:
                print("The app isn't authorized to schedule or receive notifications.")
                self.NotificationPopup()
            case .notDetermined:
                print("The user hasn't yet made a choice about whether the app is allowed to schedule notifications.")
                self.NotificationPopup()
            case .provisional:
                print("The application is provisionally authorized to post noninterruptive user notifications.")
                self.NotificationPopup()
            }
        }
        
    }
    
    private func NotificationPopup(){
        let alertController = UIAlertController(title: "Notification Alert", message: "Please Turn on the Notification to get update every time the Show Starts", preferredStyle: .alert)
        let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
            guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
                return
            }
            if UIApplication.shared.canOpenURL(settingsUrl) {
                UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
                })
            }
        }
        let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
        alertController.addAction(cancelAction)
        alertController.addAction(settingsAction)
        DispatchQueue.main.async {
            self.currentViewController?.present(alertController, animated: true, completion: nil)
            
        }
        
    }
    
    
}

to access this code on ViewController user this on viewDidLoad

NotificaionStatusCheck.shared.currentViewController(self)
Gregor answered 1/1, 2020 at 11:36 Comment(2)
for case notDetermined the permission wasn't requested yet, so whats the point of send the user to settings? It should ask for the permission is this case.Mandal
"import UIKit" must be added to use all the UI components in the code.Concupiscent
S
7

Here's a solution for getting a string describing the current permission that works with iOS 9 trough iOS 11, with Swift 4. This implementation uses When for promises.

import UserNotifications

private static func getNotificationPermissionString() -> Promise<String> {
    let promise = Promise<String>()

    if #available(iOS 10.0, *) {
        let notificationCenter = UNUserNotificationCenter.current()
        notificationCenter.getNotificationSettings { (settings) in
            switch settings.authorizationStatus {
            case .notDetermined: promise.resolve("not_determined")
            case .denied: promise.resolve("denied")
            case .authorized: promise.resolve("authorized")
            }
        }
    } else {
        let status = UIApplication.shared.isRegisteredForRemoteNotifications ? "authorized" : "not_determined"
        promise.resolve(status)
    }

    return promise
}
Spectroscope answered 20/11, 2017 at 14:59 Comment(0)
C
5

Even though user doesn't allow the push notifications, the device token is available. So it would be also a good idea to check if it's allowed to receive the push notifications.

private func checkPushNotificationAllowed(completionHandler: @escaping (Bool) -> Void) {
    if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            if settings.authorizationStatus == .notDetermined || settings.authorizationStatus == .denied {
                completionHandler(false)
            }
            else {
                completionHandler(true)
            }
        }
    }
    else {
        if let settings = UIApplication.shared.currentUserNotificationSettings {
            if settings.types.isEmpty {
                completionHandler(false)
            }
            else {
                completionHandler(true)
            }
        }
        else {
            completionHandler(false)
        }
    }
}
Counterattraction answered 15/10, 2018 at 22:27 Comment(0)
D
4
class func isRegisteredForRemoteNotifications() -> Bool {
    if #available(iOS 10.0, *) {
        var isRegistered = false
        let semaphore = DispatchSemaphore(value: 0)
        let current = UNUserNotificationCenter.current()
        current.getNotificationSettings(completionHandler: { settings in
            if settings.authorizationStatus != .authorized {
                isRegistered = false
            } else {
                isRegistered = true
            }
            semaphore.signal()
        })
        _ = semaphore.wait(timeout: .now() + 5)
        return isRegistered
    } else {
        return UIApplication.shared.isRegisteredForRemoteNotifications
    }
}
Disparate answered 26/6, 2018 at 22:22 Comment(2)
please don't do this to make a async operation appear sync -> _ = semaphore.wait(timeout: .now() + 5)Sixpenny
@Sixpenny Is there any specific reason except that async operation might take more than 5 seconds in some rare cases?Cannonade
A
4

All answers above are almost correct BUT if you have push notifications enabled and all options disabled (alertSetting, lockScreenSetting etc.), authorizationStatus will be authorized and you won't receive any push notifications.

The most appropriate way to find out if you user can receive remote notifications is to check all these setting values. You can achieve it using extensions.

Note: This solution works for iOS 10+. If you support older versions, please read previous answers.

extension UNNotificationSettings {

    func isAuthorized() -> Bool {
        guard authorizationStatus == .authorized else {
            return false
        }

        return alertSetting == .enabled ||
            soundSetting == .enabled ||
            badgeSetting == .enabled ||
            notificationCenterSetting == .enabled ||
            lockScreenSetting == .enabled
    }
}
extension UNUserNotificationCenter {

    func checkPushNotificationStatus(onAuthorized: @escaping () -> Void, onDenied: @escaping () -> Void) {
        getNotificationSettings { settings in
            DispatchQueue.main.async {
                guard settings.isAuthorized() {
                    onDenied()
                    return
                }

                onAuthorized()
            }
        }
    }
}
Anvers answered 9/12, 2019 at 11:58 Comment(1)
In second snippet there is no else in guard.Prayerful
R
3

The new style with async await

 static func getPermissionState() async throws  { 
        let current = UNUserNotificationCenter.current()
        
            let result = await current.notificationSettings()
            switch result.authorizationStatus {
            case .notDetermined:
                //
            case .denied:
                //
            case .authorized:
                //
            case .provisional:
                //
            case .ephemeral:
                //
            @unknown default:
                //
            }
       
    }
Rejoice answered 29/4, 2022 at 8:55 Comment(0)
S
1

My short asynchronously extension:

// UNUserNotificationCenter+Helper.swift

extension UNUserNotificationCenter {
    static func notificationsAllowed() async -> Bool {
        let settings = await UNUserNotificationCenter.current().notificationSettings()

        return !(settings.authorizationStatus == .notDetermined || settings.authorizationStatus == .denied)
    }
}

SwiftUI Example:

 @State private var notificationsAllowed = false

 @MainActor
 private func refresh() {
     Task {
         let notificationsAllowed = await UNUserNotificationCenter.notificationsAllowed()
        
         withAnimation {
             self.notificationsAllowed = notificationsAllowed
         }
     }
 }
Suffice answered 8/11, 2023 at 17:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.