Registering for notifications

Any app that wants to present notifications has to ask the user for permission before they do so. This process is called registering for notifications. It's important that you make sure your user understands why you're asking their permission to send notifications. After all, you're interrupting your user while they're trying to achieve something in your app, and the permission alert prevents them from doing so. It's often a great idea to hold off on asking for permission to send notifications until the user does something that can be improved specifically by turning on notifications.

In the Notifications example app, a user can add reminders for certain actions that they need to perform. The best time to ask a user for permission to send them notifications is right after they create a new reminder. This will make it clear to the user that you want to send notifications to inform the user about the reminder they just added.

When you ask for permission to send notifications, you can specify the kinds of notification that you want to send the user. You can pick from the following types:

  • Alerts
  • Badges
  • CarPlay
  • Sounds
  • Critical
  • Provisional

The user can go into your app's page in the Settings app at any time to change these settings. You can read these settings with the UserNotifications framework. By reading the current notification settings, you can inform your users about the current notification status, and ask them to turn notifications on if needed.

When you ask for a user's permission to send notifications and they deny permission to do so, or they disable notifications at a later stage, it's often a good idea to notify the user that they have notifications turned off, and then provide an easy way to turn notifications back on.

Reading the current notification settings and asking for permission to send notifications is always done through an UNUserNotificationCenter instance. You will typically obtain an instance of UNUserNotificationCenter through the static current() method.

UNUserNotificationCenter is not to be confused with NotificationCenter. UNUserNotificationCenter is responsible for notifications such as push and local notifications, while NotificationCenter is used to send notifications internally inside your app. The user never sees or notices these notifications.

As you have learned before, it's good practice to extract certain parts of your code into a dedicated helper object. Create a new helper folder in the Notifications project and add a new Swift file, called NotificationsHelper.swift, to the group. Add the following implementation code to your new helper file:

import UIKit
import UserNotifications

struct NotificationsHelper {
  let notificationCenter = UNUserNotificationCenter.current()
}

The best moment to ask a user for permission to send them push notifications for this app is when they have just added a reminder. At this point, the user will understand exactly why they see the permission dialog, because they just performed an action that is closely related to the push notifications they will receive.

Add the following method to the notifications helper to ask the user for permission to send them notifications:

func requestNotificationPermissions(_ completion: @escaping (Bool) -> ()) {
  notificationCenter.requestAuthorization(options: [.badge, .sound, .alert]) { permissionGranted, error in
    completion(permissionGranted)
  }
}

The preceding method calls the notification center's requestAuthorization(options:completion:), and calls the completion closure that was passed to requestNotificationPermissions(_:) with the result of the permission prompt.

Before you can use the helper in the add notifications view controller to ask the user for notification permissions when they add a new reminder, you need to create an instance of the helper in AddNotificationViewController. Add the following property to this class:

let notificationsHelper = NotificationsHelper()

Also, add the following code to the end of the createReminder(_:withComponents:inContext:) method to ask the user for notification permissions after creating a new reminder:

DispatchQueue.main.async { [weak self] in
  self?.notificationsHelper.requestNotificationPermissions { result in
    DispatchQueue.main.async {
      self?.enableNotificationsButton.isHidden = result
    }
  }
}

Since the code inside of the createReminder(_:withComponents:inContext:) method is executed on a managed object context's queue, it's good practice to ensure that the notification prompt will be presented using the main thread, by wrapping the code in a DispatchQueue.main.async call. To learn more about how dispatch queues work and how you can use them, refer to Chapter 28Offloading Tasks with Operations and GCD.

In addition to asking for notification permissions when the user adds a new reminder, permission should also be asked when the user enables the periodic drink water notification. Add the following implementation for drinkWaterNotificationToggled(sender:):

@IBAction func drinkWaterNotificationToggled(sender: UISwitch) {
  if sender.isOn {
    notificationsHelper.requestNotificationPermissions { [weak self] result in
      DispatchQueue.main.async {
        self?.enableNotificationsButton.isHidden = result
      }
    }
  }
}

The add notification view contains a button that prompts the user to enable notifications for the app. This button should only be visible if the user has denied the notification permissions dialog, or if the user has turned off notifications for the app in their settings. Add the following method to the notifications helper to determine whether the user has turned off notifications for the app:

func userHasDisabledNotifications(_ completion: @escaping (Bool) -> ()) {
  notificationCenter.getNotificationSettings { settings in
    completion(settings.authorizationStatus == .denied)
  }
}

The preceding code retrieves the current notification settings, and checks whether the current status is .disabled. There are a total of four possible authorization statuses: .authorized, .denied, .notDetermined, and .provisional. The following list describes what each status means:

  • notDetermined means that the app hasn't asked for permissions before.
  • authorized means that the app has asked for permissions and is allowed to send notifications to the user.
  • denied means that the app has asked for permissions to send notifications to the user, and the user denied this permission, because they either declined the permissions prompt, or they turned off notifications in their settings.
  • provisional means that the app is allowed to deliver quiet notifications to the app on a provisional basis.

It's a good idea to read the current notification settings whenever the add notification view appears, so the button that takes the user to the settings page is always shown and hidden correctly, based on the current notification permissions. Add the following implementation for viewWillAppear(_:) to the AddNotificationViewController class:

notificationsHelper.userHasDisabledNotifications { [weak self] notificationsDisabled in
  DispatchQueue.main.async {
    self?.enableNotificationsButton.isHidden = !notificationsDisabled
  }
}

Before you test the app, add the following implementation for the openSettingTapped() method.

Add the following implementation for the enableNotificationsTapped action:

@IBAction func openSettingsTapped() {
  let settingsUrl = URL(string: UIApplication.openSettingsURLString)
  UIApplication.shared.open(settingsUrl!, options: [:], completionHandler: nil)
}

The preceding implementation will take the user to the Settings app, so they can easily enable notifications for the Notifications app. If you test the app now, you can add a new reminder, and the app will ask you for permission to send notifications. If permission is denied, the enable button will appear at the bottom of the view. When you tap this button, you will be taken directly to the setting page, where you can again enable notifications.

The preceding code registers the app for local notifications. If you want to register the app for push notifications, you must enable the Push Notifications capability in the Capabilities tab. Note that you can only enable this capability if you're on Apple's paid developer program. Free developer accounts cannot enable this capability.

After enabling the push notifications capability, you need to add a couple of lines of code to make sure that you properly register the current device with APNS. To do this, you need to call a method on UIApplication that registers the current device on APNS. After calling this method, a delegate method in AppDelegate is called with the current device token that should be uploaded to your remote server, so you can send messages to the device through the device token.

Apple makes no guarantees about if and when a device's push token can change, so you should always register for remote notifications whenever the app launches. For the Notifications app, it makes sense to register for remote notifications as soon as the app enters the foreground, or when the user has just granted the app permission to send push notifications.

Add the following lines of code to the requestNotificationPermissions(_:) method in the notification helper, right after calling the completion handler:

DispatchQueue.main.async {
  UIApplication.shared.registerForRemoteNotifications()
}

Note that you must always call UIApplication.shared.registerForRemoteNotifications() on the main thread. Since the callback for requestNotificationPermissions(_:) is not guaranteed to be called on the main thread, you should manually ensure that you register for remote notifications on the main thread.

Next, implement the following methods in AppDelegate:

func applicationDidBecomeActive(_ application: UIApplication) {
  UIApplication.shared.registerForRemoteNotifications()
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  print("received device token: (deviceToken)")
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
  print("Did not register for remote notifications: (error.localizedDescription)")
}

For demo purposes, the device token is printed to the console. In a real app, you should upload the device token to your server instead. You're all set to send notifications to your users now. Next, let's look at how you can create and configure notifications in your app.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.234.225