Logo for tanaschita.com

Quick guide on local notifications for iOS

Learn how to schedule local notifications in different ways and how to handle them.

02 May 2022 · 6 min read

iOS local and push notifications allow us to keep users up to date or get reminded of time or location sensitive events, even if the app is running in the background or is inactive.

While remote push notifications are send by a server using the Apple Push Notification Service, local notifications are fully controlled and send by the app itself. Both notification types are configured by using the UserNotifications framework.

In this guide, we are going to look at how local notifications work, how to ask user's permission, how to schedule them in different ways and how to handle them.

Get user's permission

As a first step, we need to ask for the user's permission to send notifications, for example on app start or when the user activates or starts using a certain feature.

UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .badge, .sound]) { success, error in
}

As options, the requestAuthorization method takes an UNAuthorizationOptions type.

With [.alert, .badge, .sound], we ask for the ability to display alerts, to update the app’s badge and to play sounds.

There are more options available. For example, we could ask for a criticalAlert permission which would require a special entitlement issued by Apple because they bypass the device's mute and do not disturb settings. Or for the option .carPlay to display notifications in a CarPlay environment.

Create the content of a local notification

After asking for permission, the first step to send a local notification is to create its content. The content of a local notification is represented by the UNMutableNotificationContent type.

let content = UNMutableNotificationContent()
content.title = "Stay hydrated"
content.body = "It's time for a glass of water"
content.sound = UNNotificationSound(named: UNNotificationSoundName("water.wav"))

In the above example, we set the title, body and sound properties. For a native sound, we could just set it to .default.

There are more options available. For example the badge property that specifies the number to apply to the app’s icon when the notification arrives. Or the threadIdentifier property to group notifications visually. We could also set the userInfo dictionary to add some custom data we can use later when handling the notification. Checkout the UNMutableNotificationContent type for a full list of configuration possibilities.

Schedule a local notification

The second step is to schedule the local notification. The following triggers are available:

The following calendar trigger example triggers the notification every morning at 7:30.

var dateComponents = DateComponents()
dateComponents.hour = 7
dateComponents.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)

The following time interval trigger example triggers the notification in 10 minutes from now.

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10*60, repeats: false)

The following location trigger example triggers the notification when the user exits a radius of 20km around Leipzig's city center.

let center = CLLocationCoordinate2D(latitude: 51.3396955, longitude: 12.3730747)
let region = CLCircularRegion(center: center, radius: 20*1000, identifier: "leipzig")
region.notifyOnEntry = false
region.notifyOnExit = true
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)

With the notifyOnEntry and notifyOnExit properties, we specify whether the app should deliver notifications on entry, exit or both.

Register a notification request

With content and trigger of the notification created, we can now register a notification request.

let id = UUID().uuidString
let request = UNNotificationRequest(
identifier: id, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
// handle error
}
}

The adding the request to UNUserNotificationCenter, it schedules the local notification for delivery.

The identifier parameter allows us to edit or cancel a specific notification request when the conditions change or we no longer need to notify the user.

Note - the method above is also available as an asynchronous method func add(_ request: UNNotificationRequest) async throws that we can use when preferring working with async/await.

Cancel a notification request

To cancel a specific request, we pass the id we used when creating it.

UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [id])

To remove all pending requests, the notification center provides the method removeAllPendingNotificationRequests().

Receiving notifications when the app is in the foreground

When the app is running in the foreground, the notification will not be shown to the user by default.

To show the notification anyway, we can use UNUserNotificationCenterDelegate that notifies us when the notification is about to be presented.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .badge, .sound])
}

Calling the completionHandler will show the notification based on the UNNotificationPresentationOptions parameter we pass in indicating how to present a notification in a foreground app.

Instead of showing the notification, we could also implement a custom solution for example to show an app dialog that makes more sense for this specific use case.

Handle notification actions

In case the app is not in the foreground, the system shows the notification to the user. When the user taps on the delivered notification, the delegate method userNotificationCenter(_:didReceive:withCompletionHandler:) is called.

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Handle notification
completionHandler()
}

Here, we could for example use the userInfo to trigger certain flows or to show information or screens related to the notification.

We could also react a specific notification action a user chose. If you like to dive deeper on this, check out the guide on How to add and handle actions for iOS push and local notifications.

Image of books
Further reading: Swift Power Pack
For further reading, check out the Swift Power Pack books by Paul Hudson. The bundle delivers six books from beginner to advanced around Swift topics.

Newsletter

Receive a monthly newsletter about the latest articles and tips.

Image of a reading marmot

Related tags

Written by

Articles with related topics

Latest articles and tips