Home Handling Push Notification Permission Status from iOS 9 to iOS 12 (Swift)
Post
Cancel

Handling Push Notification Permission Status from iOS 9 to iOS 12 (Swift)

Handling Push Notification Permission Status from iOS 9 to iOS 12 (Swift)

Solution for handling notification permission status and requesting permissions from iOS 9 to iOS 12

What to do?

Following the previous article “What? iOS 12 can send push notifications without user authorization (Swift)” which mentioned the optimization of the push notification permission acquisition process, after the optimization written in the previous Murmur part, new requirements were encountered:

  1. If the user turns off the notification function, we can prompt them to go to settings to turn it on in a specific function page.
  2. After jumping to the settings page, if there is an operation to turn on/off notifications, the app should be able to follow and change the status.
  3. If the push notification permission has not been asked before, ask for permission; if it has been asked but not allowed, show a prompt; if it has been asked and allowed, continue to operate.
  4. Support iOS 9 to iOS 12.

Items 1 to 3 are fine, using the iOS 10 and later Framework UserNotifications can almost solve them properly. The troublesome part is item 4, which needs to support iOS 9. Handling iOS 9 using the old method registerUserNotificationSettings is not easy; let’s do it step by step!

Thought Process and Structure:

First, declare a global notificationStatus object to store the notification permission status and add property monitoring to the pages that need to handle it (here I use Observable to subscribe to property changes, you can find suitable KVO or use Rx, ReactiveCocoa).

In appDelegate, handle the check of push notification permission status and change the value of notificationStatus in didFinishLaunchingWithOptions (when the app initially opens), applicationDidBecomeActive (when returning from the background state), and didRegisterUserNotificationSettings (≤iOS 9 push notification inquiry handling).

The pages that need to handle it will trigger and perform corresponding processing (e.g., pop up a notification closed prompt).

1. First, declare the global notificationStatus object

1
2
3
4
5
6
enum NotificationStatusType {
     case authorized
     case denied
     case notDetermined
}
var notificationStatus: Observable<NotificationStatusType?> = Observable(nil)

The four states of notificationStatus/NotificationStatusType correspond to:

  • nil = Object initialization…checking…
  • notDetermined = User has not been asked whether to receive notifications
  • authorized = User has been asked whether to receive notifications and clicked “Allow”
  • denied = User has been asked whether to receive notifications and clicked “Don’t Allow”

2. Construct the method to check the notification permission status:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func checkNotificationPermissionStatus() {
    if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            DispatchQueue.main.async {
                // Note! Switch back to the main thread
                if settings.authorizationStatus == .authorized {
                    // Allowed
                    notificationStatus.value = NotificationStatusType.authorized
                } else if settings.authorizationStatus == .denied {
                    // Not allowed
                    notificationStatus.value = NotificationStatusType.denied
                } else {
                    // Not asked
                    notificationStatus.value = NotificationStatusType.notDetermined
                }
            }
        }
    } else {
        if UIApplication.shared.currentUserNotificationSettings?.types == []  {
            if let iOS9NotificationIsDetermined = UserDefaults.standard.object(forKey: "iOS9NotificationIsDetermined") as? Bool, iOS9NotificationIsDetermined == true {
                // Not asked
                notificationStatus.value = NotificationStatusType.notDetermined
            } else {
                // Not allowed
                notificationStatus.value = NotificationStatusType.denied
            }
        } else {
            // Allowed
            notificationStatus.value = NotificationStatusType.authorized
        }
    }
}

That’s not all! Sharp-eyed friends should have noticed the custom UserDefaults “iOS9NotificationIsDetermined” in the ≤ iOS 9 judgment. What is it used for?

The main reason is that the method for detecting push notification permissions in ≤ iOS 9 can only use the current permissions as a judgment. If it is empty, it means no permission, but it will also be empty if the permission has not been asked. This is troublesome because it is unclear whether the user has never been asked or has denied the permission.

Here, I use a custom UserDefaults “iOS9NotificationIsDetermined” as a judgment switch and add it in the appDelegate’s didRegisterUserNotificationSettings:

1
2
3
4
5
6
//appdelegate.swift:
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
    // For iOS 9 and below, this method is triggered after the permission prompt is shown and the user either allows or denies the notification.
    UserDefaults.standard.set("iOS9NotificationIsDetermined", true)
    checkNotificationPermissionStatus()
}

After constructing the object and method for checking notification permission status, we need to add the following in appDelegate…

1
2
3
4
5
6
7
8
//appdelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {  
  checkNotificationPermissionStatus()
  return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
  checkNotificationPermissionStatus()
}

The app needs to check the push notification status both at launch and when returning from the background.

This covers the detection part. Next, let’s see how to handle the request for notification permissions if it has not been asked.

3. Request Notification Permission:

1
2
3
4
5
6
7
8
9
10
11
12
13
func requestNotificationPermission() {
    if #available(iOS 10.0, *) {
        let permissions: UNAuthorizationOptions = [.badge, .alert, .sound]
        UNUserNotificationCenter.current().requestAuthorization(options: permissions) { (granted, error) in
            DispatchQueue.main.async {
                checkNotificationPermissionStatus()
            }
        }
    } else {
        application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil))
        // The didRegisterUserNotificationSettings in appdelegate.swift will handle the subsequent callback
    }
}

After handling detection and requests, let’s see how to apply it.

4. Application (Static)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
if notificationStatus.value == NotificationStatusType.authorized {
    // OK!
} else if notificationStatus.value == NotificationStatusType.denied {
    // Not allowed
    // This example shows a UIAlertController prompt and redirects to the settings page upon clicking
    let alertController = UIAlertController(
        title: "Dear, you are currently unable to receive notifications",
        message: "Please enable notification permissions for the app.",
        preferredStyle: .alert)
    let settingAction = UIAlertAction(
        title: "Go to Settings",
        style: .destructive,
        handler: {
            (action: UIAlertAction!) -> Void in
            if let bundleID = Bundle.main.bundleIdentifier, let url = URL(string: UIApplicationOpenSettingsURLString + bundleID) {
                UIApplication.shared.openURL(url)
            }
    })
    let okAction = UIAlertAction(
        title: "Cancel",
        style: .default,
        handler: {
            (action: UIAlertAction!) -> Void in
            // well....
    })
    alertController.addAction(okAction)
    alertController.addAction(settingAction)
    self.present(alertController, animated: true) {
        
    }
} else if notificationStatus.value == NotificationStatusType.notDetermined {
    // Not asked
    requestNotificationPermission()
}

Note!! When jumping to the “Settings” page of the APP, do not use

UIApplication.shared.openURL(URL(string:”App-Prefs:root=\ (bundleID)”) )

method to jump, it will be rejected! It will be rejected! It will be rejected! (personal experience)

This is a Private API

5. Application (Dynamic)

For dynamically changing the status, since we use the Observable object for notificationStatus, we can add a listener in viewDidLoad where we need to monitor the status in real-time:

1
2
3
4
5
6
7
8
9
10
override func viewDidLoad() {
   super.viewDidLoad()
   notificationStatus.afterChange += { oldStatus,newStatus in
      if newStatus == NotificationStatusType.authorized {
       //print("❤️Thank you for enabling notifications") 
      } else if newStatus == NotificationStatusType.denied {
       //print("😭Oh no")
      }
   }
}

The above is just sample code. You can adjust the actual application and triggers as needed.

*When using Observable for notificationStatus, please pay attention to memory management. It should be released when necessary (to prevent memory leaks) and retained when not (to avoid listener failure).

Finally, here is the complete demo product:

[Wedding App](https://itunes.apple.com/tw/app/%E7%B5%90%E5%A9%9A%E5%90%A7-%E4%B8%8D%E6%89%BE%E6%9C%80%E8%B2%B4-%E5%8F%AA%E6%89%BE%E6%9C%80%E5%B0%8D/id1356057329?ls=1&mt=8){:target="_blank"}

Wedding App

*Since our project supports iOS 9 to iOS 12, iOS 8 has not been tested and the support level is uncertain.

If you have any questions or comments, feel free to contact me.

===

本文中文版本

===

This article was first published in Traditional Chinese on Medium ➡️ View Here


This post is licensed under CC BY 4.0 by the author.

What? iOS 12 Can Receive Push Notifications Without User Authorization (Swift)

Always Keep the Enthusiasm for Exploring New Things