With iOS 12, Apple fine-tuned the notification authorization process and expanded notification delivery options, giving developers the ability to build apps with high opt-in, reaction, and retention rates, thus leading to potentially higher revenues. The company announced these new features during a WWDC 2018 presentation entitled “What’s New in User Notifications.” App developers now have the ability to start sending notifications without explicit permission, i.e., on a trial basis. Apple calls this new notification management protocol “provisional authorization” which is closely related to a feature they’ve named “deliver quietly.” In this tutorial, I’ll show you how to encode these new notification features using software released to developers just fifteen days ago (June 19), specifically iOS 12 beta 2 and Xcode 10 beta 2 (which includes Swift 4.2).
To give you an idea of the code I’ll be writing and explaining in this tutorial, here are two videos of my sample app delivering a notification provisionally on an iPhone 8 Plus. Notice iOS 12 has multiple options for configuring how future notifications will be delivered:
INTRODUCTION
Many iOS developers have traditionally prompted customers for notification authorization as soon as their apps run for the first time. To compound user discomfort, developers tend to ask for permissions to all device features on first run. Instead of making the first-time startup for new apps a gauntlet of authorization requests — “‘App Name’ Would Like to Send You Notifications,” “‘App Name’ Would Like to Access Your Contacts,” “‘App Name’ Would Like to Access the Camera” — you should not prompt customers for permission to use device features until your app actually needs those features. When displaying permission prompts, use short, well-written explanations that describe precisely why customers should tap “Allow,” extolling the benefits of authorization and enumerating exactly what your app will do with the feature(s).
Sample Xcode 10 beta 2 project
I’ve included a sample Xcode 10 project, written in Swift, so you can follow along with my discussion and code during this tutorial. You can download it from GitHub.
AUTHORIZING AND SENDING NOTIFICATIONS THE OLD WAY
To follow along with my tutorial, create a new Xcode 10 project based on the iOS Single View App
template.
As an iOS developer, you should be familiar with local and remote (push) notifications by now. While I will cover the basic setup for sending notifications, I assume you can follow along without a large amount of detailed guidance on my part.
Obviously, I have to send a local or remote notification before I can receive one. To save time and help you to concentrate on customizing notifications instead of getting lost in the intricacies of push notifications, I’m going to stick to local notifications. Notifications will look and act the same regardless of whether they are sent locally or remotely.
Requesting permission from the user
You must ask users of your app for permission to send them notifications. This is a required step. Many app designers and developers place the code for asking permission in their Xcode projects’ AppDelegate.swift
file, in the application(_:didFinishLaunchingWithOptions:)
instance method. Note that whenever referencing notifications during development, you must import UserNotifications
. Here’s the code:
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 |
... // SDK required for notifications import UserNotifications ... func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // Before your app can send any type of notification, // you have to request the permission from the user to do so. // Remember to "Request only the authorization options that you // plan to use." UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in if granted { print("Notifications permission granted.") } else { print("Notifications permission denied because: \(error?.localizedDescription).") } } return true } ... |
When the user first runs the app we’re building in this tutorial, they’ll get the following alert, and they need to tap the “Allow” button to start getting notifications:
If your customer taps “Don’t Allow” here, you’ve probably lost notifications as a type of engagement, even if notifications are an intrinsic part of your app’s workflow. The likelihood that the average user will spend the time playing with your app to figure out that they need to authorize notifications is low — unless your app is a one-in-a-million that people just have to have. If they actually do figure out that they need notifications from your app, they’re going to have take the extra step to use the iOS Settings
app to authorize notifications. Good luck with that.
According to Accengage’s “2017 Edition of the annual Push Notification Benchmark:”
… 43% of iOS users … manually accept to receive them when prompted (active consent) … The average Mobile App Push Notification reaction rate is of 8.4%. …
Think about this awful opt-in rate on iOS — and absolutely horrendous reaction rate. Hopefully, iOS 12 notification improvements will help iOS developers with notification opt-in, reaction, and app retention rates.
Note that we haven’t yet configured my sample app to send notifications yet.
Define the notification type (category identifier)
iOS requires that we define strings to distinguish between different types of notifications that our apps can send and receive. This allows us to differentiate between notifications with different user interfaces (UIs), styles, actions, etc. The Apple documentation on iOS 12 notifications is quite explicit about this step — and it’s a best practice when coding notifications.
To define a “notification’s category value–its type,” for this tutorial, I added the following code to my sample app’s ViewController.swift
file in the viewDidLoad()
method. Note that there are two lines of code, with corresponding numbered comments, that I will explain below in detail.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
... // SDK required for notifications import UserNotifications class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. // #1.1 - Create "the notification's category value--its type." let bankingAppIntroCategory = UNNotificationCategory(identifier: "com.domain.bankingAppIntro", actions: [], intentIdentifiers: [], options: []) // #1.2 - Register the notification type. UNUserNotificationCenter.current().setNotificationCategories([bankingAppIntroCategory]) } ... |
#1.1 – I’m creating an instance of the UNNotificationCategory
class using the init(identifier:actions:intentIdentifiers:options:)
initializer. Since my notification is meant to demonstrate provisional authorization and quiet notifications, all’s I need here is the identifier
argument. I leave the remaining parameters empty. Note that Apple says “You may specify nil for this parameter if you do not want to display custom actions” regarding the actions
argument, but the beta compiler will only accept an empty ([]
) array.
#1.2 – I’m calling setNotificationCategories(_:)
with my one bankingAppIntroCategory
constant of type UNNotificationCategory
to register my app’s notification type. Remember for our purposes, we’re only concerned that “Each object in the categories
parameter contains a string for identifying the notification’s type.”
Creating and sending a local notification
As stated earlier, I’m not going into enormous detail on the topic of creating and sending a notification. My code is very readable, well-commented, and you can always read up on an overview of the process at links like this one or more specifically at this link.
To check for user notification preferences, create notification content, define a trigger which determines when my notification will actually be sent, and put it all together to formally request the scheduling of my notification for sending, I added the following code to my sample app’s ViewController.swift
file in the sendNotifyButtonTapped(_:)
method, an @IBAction
connected to a single button in my app’s storyboard. The inline commentary should be enough explanation for creating and sending a notification for the purposes of this tutorial. Note that there are two lines of code, with corresponding numbered comments, that I will explain below in detail. Here’s the code:
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 35 36 37 38 39 40 41 42 43 44 |
... @IBAction func sendNotifyButtonTapped(_ sender: Any) { // find out what are the user's notification preferences UNUserNotificationCenter.current().getNotificationSettings { (settings) in // we're only going to create and schedule a notification // if the user has kept notifications authorized for this app guard (settings.authorizationStatus == .authorized) else { return } // create the content and style for the local notification let content = UNMutableNotificationContent() // #2.1 - "Assign a value to this property that matches the identifier // property of one of the UNNotificationCategory objects you // previously registered with your app." content.categoryIdentifier = "com.domain.bankingAppIntro" // create the notification's content to be presented // to the user content.title = "Welcome to app-based banking." content.subtitle = "We can keep you up-to-date." content.body = "How would like to get reminders about your bill, notices about unusual activity, free tips about security, and more?" content.sound = UNNotificationSound.default // #2.2 - create a "trigger condition that causes a notification // to be delivered after the specified amount of time elapses"; // deliver after 10 seconds let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false) // create a "request to schedule a local notification, which // includes the content of the notification and the trigger conditions for delivery" let uuidString = UUID().uuidString let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger) // "Upon calling this method, the system begins tracking the // trigger conditions associated with your request. When the // trigger condition is met, the system delivers your notification." UNUserNotificationCenter.current().add(request, withCompletionHandler: nil) } // end getNotificationSettings } // end func sendNotifyButtonTapped ... |
#2.1 – Remember that in steps #1.1 and #1.2, I assigned the string “com.domain.bankingAppIntro” as the identifier
property of a UNNotificationCategory
object and registered it with the UNUserNotificationCenter
? Regarding the categoryIdentifier
property, Apple says:
Assign a value to this property that matches the identifier
property of one of the UNNotificationCategory
objects you previously registered with your app.
#2.2 – By default, notifications won’t show up when the app is in the foreground. You can change this behavior, but we won’t get into it here. To get around that, I schedule my notification to be sent 3 seconds after its corresponding request is passed to add(_:withCompletionHandler:)
. To keep it simple, I tap the button whose @IBAction
is shown above, press the “Home” button so my “New iOS 12 Notifications” app goes into the background, and wait for the notification to appear:
ENCODING PROVISIONAL NOTIFICATION AUTHORIZATION
Asking about notifications when an app is first run, as I just did above, before a user has had a chance to evaluate an app, often leads users to tap “Don’t Allow.” You definitely want to put some thought into when you ask users if you can send them notifications.
I’m sure you could all design and write the code to wait, say, a week (7 days), between the day your app is first run and when you prompt the user for authorizing notifications, but iOS 12 provides a more elegant solution.
Apple now allows you to skirt the traditional authorization process and configure notification permissions so that your “application is provisionally authorized to post noninterruptive user notifications.”
All’s you have to do is add the .provisional
property of type UNAuthorizationOptions
to the options
argument in the call to requestAuthorization(options:completionHandler:)
. The .provisional
type property provides developers with “The ability to post noninterrupting notifications provisionally to the Notification Center.”
I modified my app startup code in file AppDelegate.swift
, as shown above, so that it now looks like this — and pay attention to line 18 which I’ve highlighted:
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 |
... // import to support notifications import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // Before your app can send any type of notification, // you have to request the permission from the user to do so. // Remember to "Request only the authorization options that you // plan to use," including "The ability to post noninterrupting // notifications provisionally to the Notification Center." UNUserNotificationCenter.current().requestAuthorization(options: [.provisional, .alert, .badge, .sound]) { (granted, error) in if granted { print("Notifications permission granted.") } else { print("Notifications permission denied because: \(error?.localizedDescription).") } } return true } ... |
I’ve just given my app the “ability to post noninterrupting notifications provisionally to the Notification Center.” When the user runs my app for the first time, he/she will not get the usual notifications permission prompt — but the following message is printed to Xcode 10’s console:
1 |
Notifications permission granted. |
Updating permissions to send a notification
Before we try the modified app out, there’s one thing we need to do when checking if we have the permission to send a notification. Look at my sample app’s sendNotifyButtonTapped(_:)
method in the ViewController.swift
file shown above. Remember that we haven’t yet asked for, nor been granted, explicit permission to send notifications. We need to modify the guard
statement as follows — and pay attention to lines 9 and 10 which I’ve highlighted:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
... @IBAction func sendNotifyButtonTapped(_ sender: Any) { // find out what are the user's notification preferences UNUserNotificationCenter.current().getNotificationSettings { (settings) in // we're only going to create and schedule a notification // if the user has kept notifications authorized for this app guard (settings.authorizationStatus == .authorized || settings.authorizationStatus == .provisional) else { return } // create the content and style for the local notification let content = UNMutableNotificationContent() ... |
Testing provisional notification authorization
Right after this new version of my sample app is installed and until the user specifies otherwise, all notifications will go to the Notification Center on the device, always available by swiping/dragging down from the top of the screen. I send a notification after my app starts as before, but it doesn’t appear on any of the device’s normal or lock screens as before. I have to check the Notification Center subsequent to first app startup as shown here:
FINE-TUNING NOTIFICATION DELIVERY
With iOS 12, the user has a number of options for precisely tuning how notifications are delivered on their devices. The option of interest today is “Deliver Quietly,” but we’ll cover all possibilities.
The “Deliver Quietly” option
Using the .provisional
type property as I showed above puts notifications into “Deliver Quietly” mode. Let’s look at this mode again as we need to discuss the two new buttons now attached to the bottom of our notifications, “Keep…” and “Turn off…”, and we need to talk about swiping left on a “Deliver Quietly” notification:
“Deliver Quietly” means that notifications will only be delivered to the Notification Center silently, with no sound effects, even if you send notifications configured with a valid sound
property of a UNMutableNotificationContent
object.
Swiping left on a “Deliver Quietly” notification
Let’s see how swiping left on a “Deliver Quietly” notification gives us access to the iOS 12 Settings
app:
This is how a “Deliver Quietly” notification is configured when you view it in the Settings
app:
After swiping left on the notification, if you tap on the “View” button, that that just brings your notification into the foreground. Note that the notification can possibly display a new iOS 12 custom UI (see my article here on how to build a custom notification UI). If you tap on “Clear” or “Clear All,” you can delete a notification or all previous notifications delivered by the app, respectively.
Let’s again swipe left on a “Deliver Quietly” notification, but this time tap the “Manage” button:
If we tap the “Turn off…” button, we are prompted with “Are you sure you want to turn off all notifications from New iOS 12 Notifications?” We can then tap “Turn Off All Notifications,” which will disable all notifications from this app until configured otherwise in the Settings
app:
If we tap the “Deliver Prominently” button, notifications for this app are delivered with everything goes options — sounds are played and the notification goes to all screens — like in the old days.
The “Deliver Prominently” option
Here’s how a “Deliver Prominently” notification is configured when you view it in the Settings
app:
The “Deliver Quietly” buttons: “Keep…” and “Turn off…”
As you saw in the previous section, “Deliver Quietly” notifications have “Keep…” and “Turn off…” buttons. Here’s what happens when you tap the the “Keep…” button:
If you tap the “Continue Delivering Quietly” button, obviously notifications for this category type will only be delivered to the Notification Center silently. If you tap the “Deliver Prominently” button, notifications for this category type will be delivered everywhere, with sounds (if applicable), as we saw previously.
If you tap the “Turn off…” button, you’ll see this screen:
You can then tap “Turn Off All Notifications” which will disable all notifications from this app until configured otherwise in the Settings
app.
CONCLUSION
Getting your users to engage is one of the keys to successful apps. If you can gently introduce your users to notifications, and use them judiciously, your customers are more likely to see your app as an elegant service, not an annoyance.
With iOS 12, Apple has given us the ability to do just that, making notifications into an elegant service. When your app is first installed, and even if you have an onboarding process, I would urge you to consider the use of “Deliver Quietly” notifications. I also encourage you to carefully consider all requests for authorization to use device features. You should wait until a feature is actually needed before asking for permission to use that feature from the user. You never want your app to be considered a nuisance by your customers.
If you follow the best practices I’ve discussed and explained in this article, you’re more likely to build apps with high opt-in, reaction, and retention rates, thus leading to potentially higher revenues from your apps.
hi,
I am not able to display Keep and turn off buttons in notification but I am able to create provisional notification. On tapping Manage button I see ‘Deliver prominently’ and ‘Turn Off’ as expected. Has apple removed Keep and turn Off buttons ?
Have you built my sample code and tried it? I used Xcode 10 beta 2. Maybe Apple changed something in Xcode 10 beta 6? I’m not sure.
Great article Andrew!
One question: Can you send provisional notifications to a user even after he pressed “Don’t Allow” in a classic permissions alert in the app? Or is the door totally shut in that case, even for provisional notifications?
If you tap the “Turn off…” button, you are prompted with “Are you sure you want to turn off all notifications from [App Name]?” You can then tap “Turn Off All Notifications,” which will disable all notifications from this app until configured otherwise in the Settings app. If your customer taps “Don’t Allow,” you’ve probably lost notifications as a type of engagement, even if notifications are an intrinsic part of your app’s workflow. The door is totally shut until you (your customer) go the Settings app and configure otherwise.
Thanks Andrew, seems logical!
Thanks for taking the time to engage on my blog, Dann. I’m so glad that Apple introduced provisional notifications so that you can ease customers into accepting notifications. Before iOS 12, customers either accepted intrusive notifications or they didn’t. Provisional ones allow you to give customers some time to experiment and decide if they want notices. But, once a customer clicks “Turn off…” or “Don’t Allow,” the likelihood that they’ll ever turn them back on is low.