UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Acting on responses

There’s a lot more you can do with notifications, but chances are the thing you most want to do is act on the user’s response – to show one or more options alongside your alert, then respond to the user’s choice.

We already set the categoryIdentifier property for our notification, which is a text string that identifies a type of alert. We can now use that same text string to create buttons for the user to choose from, and iOS will show them when any notifications of that type are shown.

This is done using two new classes: UNNotificationAction creates an individual button for the user to tap, and UNNotificationCategory groups multiple buttons together under a single identifier.

For this technique project we’re going to create one button, “Show me more…”, that will cause the app to launch when tapped. We’re also going to set the delegate property of the user notification center to be self, meaning that any alert-based messages that get sent will be routed to our view controller to be handled.

Creating a UNNotificationAction requires three parameters:

  1. An identifier, which is a unique text string that gets sent to you when the button is tapped.
  2. A title, which is what user’s see in the interface.
  3. Options, which describe any special options that relate to the action. You can choose from .authenticationRequired, .destructive, and .foreground.

Once you have as many actions as you want, you group them together into a single UNNotificationCategory and give it the same identifier you used with a notification.

That’s it! Add this method to ViewController now:

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])
}

You might have noticed the empty intentIdentifiers parameter in the category initializer - this is used to connect your notifications to intents, if you have created any.

You’ll get an error because you assigned self to be the delegate of the user notification center. To fix it, make the ViewController class conform to UNUserNotificationCenterDelegate like this:

class ViewController: UIViewController, UNUserNotificationCenterDelegate {

You can call registerCategories() wherever you want, but in this project the safest place is probably right at the beginning of the scheduleLocal() method.

Now that we have registered the “alarm” category with a single button, the last thing to do is implement the didReceive method for the notification center. This is triggered on our view controller because we’re the center’s delegate, so it’s down to us to decide how to handle the notification.

We attached some customer data to the userInfo property of the notification content, and this is where it gets handed back – it’s your chance to link the notification to whatever app content it relates to.

When the user acts on a notification you can read its actionIdentifier property to see what they did. We have a single button with the “show” identifier, but there’s also UNNotificationDefaultActionIdentifier that gets sent when the user swiped on the notification to unlock their device and launch the app.

So: we can pull out our user info then decide what to do based on what the user chose. The method also accepts a completion handler closure that you should call once you’ve finished doing whatever you need to do. This might be much later on, so it’s marked with the @escaping keyword.

Here’s the code – add this method to ViewController now:

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…")

        default:
            break
        }
    }

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

Our project now creates notifications, attaches them to categories so you can create action buttons, then responds to whichever button was tapped by the user – we’re done!

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.5/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.