2021 Graduation Book Sale – 50% Off!

Our entire catalog of online books is on sale for 50% off for a
limited time. It’s our 2021 Graduation Sale – come see what’s new!😉

Home iOS & Swift Tutorials

Local Notifications: Getting Started

Learn how to create notifications by time intervals, time of day and location, as well as how to support category grouping and prompting for action.

5/5 3 Ratings

Version

  • Swift 5, iOS 14, Xcode 12

Notifications are an integral way of conveying information to the user outside of an app. They help draw users’ attention and drive their engagement with the app. But a burst of notifications can annoy users, causing them to turn them off or even delete the app. So you must use notifications wisely.

There are two types of notifications: local and remote. This tutorial is all about local notifications. In this tutorial, you’ll learn how to:

  • Request permission for notifications.
  • Create, schedule and manage different types of local notifications.
  • Handle notification actions.

Often, users forget about tasks and need a reminder. You’ll make it easy to remind them using the power of local notifications.

Getting Started

Download the project by clicking the Download Materials button at the top or bottom of this page. Open the OrganizerPlus project. Build and run.

OrganizerPlus Overview

OrganizerPlus helps keep track of your tasks. Tap the + button to start creating a task. Next, add a name for the task and tap Save. You can mark the task as completed by tapping the circle next to the task.

Introducing Notifications

Notifications can be either local or remote. The app on the device schedules and configures local notifications. In contrast, a server sends remote notifications using Apple Push Notification Service (APNS). A news alert is an example of a remote notification where the server sends the latest breaking news to the device as a notification.

Remote notifications can be both visible and silent. Silent notifications launch the app in the background to perform an action, such as refreshing the app. You can configure both local and remote notifications using the UserNotifications framework. To learn more about remote notifications, check out Push Notifications Tutorial: Getting Started.

Contrasting Local and Push Notification Processes

To create and manage notifications, you need the following steps:

  • Authorization
  • Content creation
  • Scheduling the notifications
  • Managing the notifications
  • Handling actions

You’ll learn how to do each of these steps in this tutorial.

Requesting Notification Permission

Notifications interrupt the user, so an app needs the user to allow them. In OrganizerPlus, you’ll request notification permission when the user taps the bell button at the top right.

First, open NotificationManager.swift. Then, add the following to NotificationManager:

func requestAuthorization(completion: @escaping  (Bool) -> Void) {
  UNUserNotificationCenter.current()
    .requestAuthorization(options: [.alert, .sound, .badge]) { granted, _  in
      // TODO: Fetch notification settings
      completion(granted)
    }
}

Here’s what the code does:

  • UNUserNotificationCenter handles all notification-related behavior in the app. This includes requesting authorization, scheduling delivery and handling actions. You can access the shared instance of UNUserNotificationCenter by calling current().
  • You invoke requestAuthorization(options:completionHandler:) to request authorization to show notifications. options denotes the notification’s behavior, such as displaying an alert, playing a sound or updating the app’s badge. You can learn more about the various UNAuthorizationOptions in the developer documentation.
  • The completion handler receives a Boolean that indicates whether the user granted the authorization. Here, you call the completion handler with the Boolean value. The TODO comment is a placeholder to fetch notification settings. You’ll implement this next.

Now, add the following in NotificationManager:

func fetchNotificationSettings() {
  // 1
  UNUserNotificationCenter.current().getNotificationSettings { settings in
    // 2
    DispatchQueue.main.async {
      self.settings = settings
    }
  }
}

Here’s what you added:

  • UNUserNotificationCenter‘s getNotificationSettings(completionHandler:) requests the notification settings authorized by the app. The settings return asynchronously. UNNotificationSettings manages all the notification-related settings and the authorization status of the app. settings is an instance of it.
  • The completion block may be called on a background thread. Here, you update the settings property on the main thread as changing its value updates the UI.

Now, replace // TODO: Fetch notification settings in requestAuthorization(completion:) with the following:

self.fetchNotificationSettings()

Here, you fetch the notification settings after the user has granted the authorization.

Prompting Notification Authorization

Open TaskListView.swift. Then, find // TODO: Add Notification authorization in the action closure of the button and replace it with the following:

// 1
NotificationManager.shared.requestAuthorization { granted in
  // 2
  if granted {
    showNotificationSettingsUI = true
  }
}

Here’s a breakdown:

  1. You request the notification authorization by calling requestAuthorization(completion:), which you defined in NotificationManager.
  2. If the user granted permission, you set showNotificationSettingsUI to true. This presents NotificationSettingsView as a sheet.

Build and run.

Notification Permissions Prompt

Tap the bell icon at the top right. This prompts the notification authorization. Next, select Allow. You’ll see the list of notification settings granted by the app.

Nice job getting through the first step of notification management!

Understanding Critical Notifications

In the notification settings, notice that the app doesn’t have authorization for critical alerts. But what are critical alerts?

Critical alerts are health, medical, security or public safety notifications. They bypass the do-not-disturb and ringer switch and play a sound. These are quite disruptive, so not all apps can send critical alerts.

If your app needs to send them, you can apply for entitlement on the Apple developer portal.

Reminders from OrganizerPlus are important but not critical, so you don’t need to enable critical alerts for this tutorial. :]

Now, it’s time to create and schedule notifications.

Creating Notifications

You can create and schedule local notifications using any of the following triggers:

  • Time interval
  • Calendar
  • Location

These options determine when your app delivers the notification. For your next step, you’ll learn to create notifications using each of these triggers.

Triggering TimeInterval Notifications

Open NotificationManager.swift and add the following to NotificationManager:

// 1
func scheduleNotification(task: Task) {
  // 2
  let content = UNMutableNotificationContent()
  content.title = task.name
  content.body = "Gentle reminder for your task!"

  // 3
  var trigger: UNNotificationTrigger?
  switch task.reminder.reminderType {
  case .time:
    if let timeInterval = task.reminder.timeInterval {
      trigger = UNTimeIntervalNotificationTrigger(
        timeInterval: timeInterval,
        repeats: task.reminder.repeats)
    }
  default:
    return
  }

  // 4
  if let trigger = trigger {
    let request = UNNotificationRequest(
      identifier: task.id,
      content: content,
      trigger: trigger)
    // 5
    UNUserNotificationCenter.current().add(request) { error in
      if let error = error {
        print(error)
      }
    }
  }
}

Here’s what you added:

  1. scheduleNotification(task:) takes in a parameter of type Task. That’s your model, which holds all the data related to any task. It’s defined in Task.swift.
  2. You start creating a notification by populating the notification content. UNMutableNotificationContent holds the payload for a local notification. Here, you populate the title and body of the notification. UNMutableNotificationContent has other properties, such as:
    • subtitle: A secondary description for the notification.
    • badge: The number to display for the app’s icon badge.
    • sound: The sound to play during notification delivery.
    • attachments: An array of attachments to display in the notification. These could be images, video or audio files.
  3. UNNotificationTrigger is an abstract class that triggers the delivery of a notification. Here, you check if the reminderType of the task is time-based with a valid time interval. Then, you create a time interval-based notification trigger using UNTimeIntervalNotificationTrigger. You use this type of trigger to schedule timers. In addition to taking a timeInterval, the constructor takes in a Boolean parameter, repeats. This determines whether the notification needs to reschedule after being delivered. You’ll handle the other cases of the switch later in this tutorial.
  4. After trigger definition, the next step is to create a notification request. You create a new request using UNNotificationRequest and specifying an identifier, content and a trigger. Each task you create already has a unique identifier. You’ll pass that as the notification identifier.
  5. Then, you schedule the notification by adding the request to UNUserNotificationCenter. The completion handler has an error object that indicates if a problem occurred when scheduling a notification.

Scheduling the Notification

Open TaskManager.swift. Add the following to save(task:) after the DispatchQueue.global().async block and before the method ends:

if task.reminderEnabled {
  NotificationManager.shared.scheduleNotification(task: task)
}

Here, you schedule a notification if the task has reminderEnabled set to true.

Build and run.

Creating a Timed Task

Received Notification

Follow these steps to add a task and test your notification:

  1. Tap the + button to start adding a task.
  2. Give the task a name.
  3. Toggle the Add Reminder switch.
  4. Use the default selection Time with the Time Interval set to one minute.
  5. Leave Repeat Notification off.
  6. Tap Save at the top right to create the task.

Finally, background the app and wait for one minute. You’ll see the notification appear.

Congratulations on your first notification! :]

To view this notification, you had to background the app and wait. Wouldn’t it be cool to view the notification while the app is still in the foreground? You’ll do that next.

Viewing Notification When the App Is in the Foreground

Open AppDelegate.swift and add the following to the end of the file:

// MARK: - UNUserNotificationCenterDelegate
extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void
  ) {
    completionHandler(.banner)
  }
}

userNotificationCenter(_:willPresent:withCompletionHandler:) asks the delegate how to handle a notification when the app is in the foreground. Here, you call the completion handler with the presentation option set to a banner. You can also add other options, such as sound and badge.

Next, add the following to AppDelegate:

private func configureUserNotifications() {
  UNUserNotificationCenter.current().delegate = self
}

Here, you declare configureUserNotifications(), which makes AppDelegate the delegate for UNUserNotificationCenter.

Now, add the following to application(_:didFinishLaunchingWithOptions:) before returning true:

configureUserNotifications()

Here, you call configureUserNotifications() to set the notification delegate as soon as the app launches.

Build and run.

Notification Shown in Foreground

To try it out, add a task with a time interval set to one minute and leave the app in the foreground. You’ll see a notification appear while the app is in the foreground. Great job!

Marking a task done deletes it, but what about the notification? You don’t want to see a reminder for a task that isn’t present. You’ll work on that next.

Removing Scheduled Notifications

Open NotificationManager.swift and add the following to NotificationManager:

func removeScheduledNotification(task: Task) {
  UNUserNotificationCenter.current()
    .removePendingNotificationRequests(withIdentifiers: [task.id])
}

You need to ensure the removal of any pending notification upon task completion. Do this by using removePendingNotificationRequests(withIdentifers:). Here, you pass the identifier of the task in an array. This is especially useful for tasks that have repeats set to true.

Next, open TaskManager.swift and add the following to remove(task:) before returning:

if task.reminderEnabled {
  NotificationManager.shared.removeScheduledNotification(task: task)
}

This removes any scheduled notification once the task finishes.

Build and run.

Removing Notification

Give this a try by creating a task with a time interval of one minute. This time, toggle the Repeat Notification switch to enable it. You’ll notice the notification appears every minute. Next, mark the created task complete by tapping the circle button next to the task. This will delete the task and you’ll no longer see the scheduled notifications for this task.

You can use removeAllPendingNotificationRequests() on UNUserNotificationCenter to unschedule all the pending notifications. removeAllDeliveredNotifications() will remove all the delivered notifications.

The time interval is just one way to trigger a notification. There are other ways to trigger notifications locally: calendar and location. You’ll implement these next.

Triggering Calendar Notifications

Open NotificationManager.swift. Then, add the following to scheduleNotification(task:) as an additional case in switch task.reminder.reminderType:

case .calendar:
  if let date = task.reminder.date {
    trigger = UNCalendarNotificationTrigger(
      dateMatching: Calendar.current.dateComponents(
        [.day, .month, .year, .hour, .minute],
        from: date),
      repeats: task.reminder.repeats)
  }

With the code you added, you:

  1. Check if the reminder of the task has a date set.
  2. Create a notification trigger of type UNCalendarNotificationTrigger. The calendar trigger delivers a notification based on a particular date and time. It extracts dateComponents from the date the user selected. Specifying only the time components will trigger a notification at that specified time.

Build and run.

Creating Calendar Notification

Received Calendar Notification

Follow these steps to create a task:

  1. Tap the + button.
  2. Give the task a name.
  3. Toggle the Add Reminder switch.
  4. Tap the Date trigger in the segment bar.
  5. Select a particular date and time — maybe one minute from now.
  6. Leave Repeat Notification off.
  7. Tap the Save button to create the task.

You’ll see the notification appear. Congratulations on your first calendar notification!

Calendar notifications let you schedule a recurring reminder of a daily routine, such as drinking water every morning.

Now, it’s time to add a reminder to buy GME stock when you pass a GameStop store. :] Enter location-based reminders!

Triggering Location Notifications

The location-based trigger causes the app to deliver a notification when the user enters or exits a region. Behind the scenes, iOS uses geofences for region monitoring. As such, the system limits the number of location-based triggers that the user can schedule at the same time.

The first step to adding a location-based notification is to request location permission. Because the system monitors the regions, the app needs the When-In-Use authorization. The location authorization and delegate code already exist for you in LocationManager.swift.

Open CreateTaskView.swift. Then, find and replace // TODO: Request location authorization with the following:

locationManager.requestAuthorization()

Here, you request location authorization when the user taps the Request Location Authorization button — the user hasn’t already granted authorization.

Creating a Location Trigger

Open NotificationManager.swift. Next, add the following to scheduleNotification(task:) by replacing the default case in switch task.reminder.reminderType:

case .location:
  // 1
  guard CLLocationManager().authorizationStatus == .authorizedWhenInUse else {
    return
  }
  // 2
  if let location = task.reminder.location {
    // 3
    let center = CLLocationCoordinate2D(
      latitude: location.latitude,
      longitude: location.longitude)
    let region = CLCircularRegion(
      center: center,
      radius: location.radius,
      identifier: task.id)
    trigger = UNLocationNotificationTrigger(
      region: region,
      repeats: task.reminder.repeats)
  }

Here’s a step-by-step breakdown:

  1. You check if the user has granted at least When In Use location authorization.
  2. Then, you check to make sure the location data exists for the task reminder.
  3. You create location-based triggers using UNLocationNotificationTrigger. First, you define a center denoted by CLLocationCoordiante2D. Using this, you create an instance of CLCircularRegion by specifying the radius and a unique identifier. Finally, you create the trigger using this circular region. You can specify whether to trigger the notification only when entering the region or when exiting the region by using notifyOnEntry and notifyOnExit on CLCircularRegion.

Build and run.

Creating Location Notification

Tap the + button to start adding a task. Next, tap Location in the segment bar. Then, tap Request Location Authorization if you haven’t granted location authorization already. Now, select Allow While Using App in the location permission prompt. You’ll see Latitude, Longitude and Radius text fields appear.

Enter the latitude and longitude of a place you plan to visit soon or the coordinates of your home. Enter 500 for the radius. This value is in meters. Then, tap Save to create the task.

You’ll now receive a notification when you enter or exit the location you specified. To simulate this, go to the simulator Features menu, select Location ▸ Custom Location… and enter the same latitude and longitude values at the prompt. You’ll see the notification appear from your app in the background. Way to go!

So far, you’ve created notifications based on different triggers. But it would be cool to group these notifications based on the trigger, right? You’ll do that next.

Grouping Notifications

By default, you group all an app’s notifications based on the app’s bundle identifier. However, you can create custom groups for your app. A good example of this is a messaging app that groups the notifications for different message groups.

For OrganizerPlus, you’ll group notifications based on the trigger type.

Open NotificationManager.swift and add the following before NotificationManager:

enum NotificationManagerConstants {
  static let timeBasedNotificationThreadId =
    "TimeBasedNotificationThreadId"
  static let calendarBasedNotificationThreadId =
    "CalendarBasedNotificationThreadId"
  static let locationBasedNotificationThreadId =
    "LocationBasedNotificationThreadId"
}

Here, you define the constants you’ll use to group the notifications based on the notification trigger type.

Find switch task.reminder.reminderType in scheduleNotification(task:). Add the identifier for each case by setting threadIdentifier for the notification content, as shown below:

switch task.reminder.reminderType {
case .time:
  content.threadIdentifier =
    NotificationManagerConstants.timeBasedNotificationThreadId
  // ...
case .calendar:
  content.threadIdentifier =
    NotificationManagerConstants.calendarBasedNotificationThreadId
  // ...
case .location:
  content.threadIdentifier =
    NotificationManagerConstants.locationBasedNotificationThreadId
  // ...
}

threadIdentifier helps group related notifications. Here, you set the identifier for the notification content based on reminderType.

Build and run.

Grouped Notifications

Create a few tasks with different triggers. When the notifications appear, you’ll see them grouped based on the type of trigger.

You can choose to turn off notification grouping for an app. To do so:

  1. Go to Settings ▸ Select the app.
  2. Tap Notifications.
  3. Tap Notification Grouping.
  4. Set grouping to Off.

This turns off grouping. The notifications will appear in the order they’re received.

Tapping a notification opens the app. Is there a way to act on a notification without opening the app? You’ll explore that next.

Handling Notification Actions

Actionable notifications allow the user to perform actions on a notification without opening the app. An actionable notification can display one or more buttons in addition to the content.

To support actionable notifications, you’ll need to:

  1. Declare the notification category at the launch of the app.
  2. Create and assign actions to the notification category.
  3. Assign the category identifiers to the notification payload.
  4. Handle the registered actions.

You’ll implement each of these next.

Declaring Category and Creating Actions

Open AppDelegate.swift. Then, add the following to configureUserNotifications() below UNUserNotificationCenter.current().delegate = self:

// 1
let dismissAction = UNNotificationAction(
  identifier: "dismiss",
  title: "Dismiss",
  options: [])
let markAsDone = UNNotificationAction(
  identifier: "markAsDone",
  title: "Mark As Done",
  options: [])
// 2
let category = UNNotificationCategory(
  identifier: "OrganizerPlusCategory",
  actions: [dismissAction, markAsDone],
  intentIdentifiers: [],
  options: [])
// 3
UNUserNotificationCenter.current().setNotificationCategories([category])

Here’s a step-by-step breakdown:

  1. You declare two actions: dismissAction and markAsDone. These will display as action buttons with the notification. Each instance of UNNotificationAction has an identifier, title and array of options. The identifier uniquely identifies the action. The title represents the text on the button. options denotes the behavior associated with the action. The different options are:
    • authenticationRequired: A user can perform this action only when the device is unlocked. If the device is locked, the system prompts the user to unlock it.
    • destructive: This action performs a destructive task. The button for this option displays with special highlighting.
    • foreground: This option causes the app to launch in the foreground.
  2. Here, you define a notification category. UNNotificationCategory defines the type of notifications an app can receive. identifier uniquely identifies the category. intentIdentifiers let the system know that the notification relates to a request made by Siri. The options denote how to handle the notifications associated with them. You can learn more about them Apple developer documentation.
  3. Here, you register the new actionable notification.

Now the system knows about your notification category and which actions it has, but an app may have more than one category for notifications and not every category will have actions assigned to it. To let the system know that it should show your actions, you must assign your notifications to this category.

Assigning Identifiers to Notification Payload

Open NotificationManager.swift. Then, add the following in scheduleNotification(task:) below content.body = "Gentle reminder for your task!":

content.categoryIdentifier = "OrganizerPlusCategory"
let taskData = try? JSONEncoder().encode(task)
if let taskData = taskData {
  content.userInfo = ["Task": taskData]
}

Here, you set the notification content’s categoryIdentifier to the identifier you used when you created the instance of UNNotificationCategory. Then, you encode the task data and assign it to the notification content’s userInfo. The app will be able to access this content when a user acts on the notification.

Handling Actions

Open AppDelegate.swift. Add the following to the AppDelegate extension:

// 1
func userNotificationCenter(
  _ center: UNUserNotificationCenter,
  didReceive response: UNNotificationResponse,
  withCompletionHandler completionHandler: @escaping () -> Void
) {
  // 2
  if response.actionIdentifier == "markAsDone" {
    let userInfo = response.notification.request.content.userInfo
    if let taskData = userInfo["Task"] as? Data {
      if let task = try? JSONDecoder().decode(Task.self, from: taskData) {
        // 3
        TaskManager.shared.remove(task: task)
      }
    }
  }
  completionHandler()
}

Here’s a code breakdown:

  1. iOS calls userNotificationCenter(_:didReceive:withCompletionHandler:) when the user acts on the notification.
  2. Check if the response’s actionIdentifier is set to markAsDone. Then, you decode the task from userInfo.
  3. After the decode succeeds, you remove the task using the shared instance of the TaskManager.

Build and run.

Handling Notification Actions

Create a task by selecting the time trigger. Long-press the notification when it appears. You’ll see two buttons with the notification. Tap the Mark as Done button. The task is removed without opening the app.

Great job! You’ve now become a pro in local notifications.

Where to Go From Here?

Download the project by clicking the Download Materials button at the top or bottom of this page.

In this tutorial, you’ve learned to create, schedule, group and handle local notifications. You can customize the appearance of notifications using a Notification Content Extension. Push Notifications Tutorial for iOS: Rich Push Notifications covers that in detail.

I hope you’ve enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below.

Average Rating

5/5

Add a rating for this content

3 ratings

More like this

Contributors

Comments