NEW: Master Swift design patterns with my latest book! >>

How to set local alerts using UNNotificationCenter

Written by Paul Hudson    @twostraws

Local notifications are messages that appear on the user's lock screen when your app isn't running. The user can then swipe to unlock their device and go straight to your app, at which point you can act on the notification.

All this is done using the User Notifications framework, so import that now:

import UserNotifications

To begin with, you need to ask for permission in order to show messages on the lock screen. Here's how that's done:

let center = UNUserNotificationCenter.current()

center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
    if granted {
        print("Yay!")
    } else {
        print("D'oh")
    }
}

That will show an alert to the user asking them if they want to let you show notifications. When it comes to scheduling a notification, you need to choose a trigger (when to show) and content (what to show), then combine them together into a single request.

Here's the code required to show a local notification:

func scheduleNotification() {
    let center = UNUserNotificationCenter.current()

    let content = UNMutableNotificationContent()
    content.title = "Late wake up call"
    content.body = "The early bird catches the worm, but the second mouse gets the cheese."
    content.categoryIdentifier = "alarm"
    content.userInfo = ["customData": "fizzbuzz"]
    content.sound = UNNotificationSound.default()

    var dateComponents = DateComponents()
    dateComponents.hour = 10
    dateComponents.minute = 30
    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)

    let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
    center.add(request)
}

The trigger constant is being created using a time interval, which means this notification will appear five seconds after you schedule it.

If you want a specific time, use UNCalendarNotificationTrigger instead, like this:

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

That will show an alert at 10:30am every day, because its repeats property is set to true.

The notification request code above also set a userInfo property on the notification, which is a dictionary where you can store any kind of context data you want. This dictionary gets given back to you when the user unlocks their device using your notification, so you can act on the alert in a meaningful way.

If you want to attach custom buttons to your notification, you need to use the UNNotificationAction class, then register various actions against a category string. We used the category identifier string of “alarm” above, so we could attach a button to that category like this:

func registerCategories() {
    let center = UNUserNotificationCenter.current()
    center.delegate = self

    let show = UNNotificationAction(identifier: "show", title: "Tell me more…", options: .foreground)
    let category = UNNotificationCategory(identifier: "alarm", actions: [show], intentIdentifiers: [])

    center.setNotificationCategories([category])
}

Note that that code sets self to be the delegate for the notification center, so you’ll need to make your view controller conform to the UNUserNotificationCenterDelegate protocol like this:

class ViewController: UIViewController, UNUserNotificationCenterDelegate {

When a message comes in, your delegate will get notified and you can take the appropriate action. We gave the “Tell me more…” button the identifier “show”, so that’s what will be passed to us if the user taps that button. Alternatively, we’ll be sent UNNotificationDefaultActionIdentifier to mean “the user swiped to unlock using our notification.”

Here’s some code handling both options:

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

    if let customData = userInfo["customData"] as? String {
        print("Custom data received: \(customData)")

        switch response.actionIdentifier {
        case UNNotificationDefaultActionIdentifier:
            // the user swiped to unlock
            print("Default identifier")

        case "show":
            // the user tapped our "show more info…" button
            print("Show more information…")
            break

        default:
            break
        }
    }

    // you must call the completion handler when you're done
    completionHandler()
}

That code also pulls out the CustomField1 value that was set in the userInfo earlier.

Available from iOS 8.0 – see Hacking with Swift tutorial 21

Did this solution work for you? Please pass it on!

Other people are reading…

About the Swift Knowledge Base

This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.

Hacking with watchOS

Transfer your Swift skills to watchOS the easy way, and learn to build real-world apps in the process!

Click here to visit the Hacking with Swift store >>