Chapters

Hide chapters

Push Notifications by Tutorials

Fourth Edition · iOS 16 · Swift 5 · Xcode 14

Section I: Push Notifications by Tutorials

Section 1: 15 chapters
Show chapters Hide chapters

11. Custom Interfaces
Written by Scott Grosch

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the last few chapters, you worked through most types of notifications, including those that present an attachment, such as an image or video, alongside the banner message; but if you really want to go hog wild, you can even customize the way the notification itself looks to your heart’s content! This can get quite complex, but it is worth the time to make an app that really shines. Custom interfaces are implemented as separate targets in your Xcode project, just like the service extension.

Your top-secret agency wants to send you the locations of your targets, so you’ll need to build a way to do that. In this chapter, you’ll create a notification that displays a location on the map, with the ability to comment on that location right from the notification, all without opening the app.

Configuring Xcode for a Custom Notification UI

After opening up the starter project for this chapter, set the team signing as discussed in Chapter 7, “Expanding the Application”. Don’t forget to also set the team signing for the Payload Modification target just as you did in the previous chapter, Chapter 10, “Modifying the Payload”.

First, you’ll create a new Notification Content Extension that will handle showing your custom UI.

  1. In Xcode, select File ▸ New ▸ Target….
  2. Makes sure iOS is selected and choose the Notification Content Extension.
  3. Press Next.
  4. For the Product Name field type Custom UI.
  5. Press Finish.
  6. If asked about scheme activation, select Cancel.

Note: You don’t actually run a Notification Content Extension, so that’s why you didn’t let it make the new target your active scheme.

You can name the new target anything that makes sense for you, but it can be helpful to use the above name because, when you glance at your project, you will immediately know what that target is doing.

Custom interfaces are triggered by specifying a category, just as you learned about with custom actions in Chapter 9, “Custom Actions”.

Every custom UI must have its own unique category identifier. Bring up the Project navigator (⌘ + 1) and select your project. Then, select the newly created target and go to the Info tab. You’ll see an item labeled NSExtension. Expand that all the way out and find a key labeled UNNotificationExtensionCategory. This identifier connects your main target, registering the identifier, with the correct content extension.

If your push notification contains a category key that matches this, the UI in your content extension will be used. Update this value to ShowMap.

If you have multiple category types that will all use the same UI, simply change the type of UNNotificationExtensionCategory from String to Array and list each category name that you’d like to support.

Designing the Interface

You’ll notice that your new target includes a storyboard and view controller for you to utilize. Wait…storyboard? But we want SwiftUI!

Decoding the Payload

When the notification arrives, you’ll need to decode the payload to get the coordinates. You’ll be working with MapKit, so in NotificationViewController.swift, add the follow two imports:

Adding a UIHostingController

A custom UI for a push notification is required to use UIKit and a UIViewController. However, with a bit of magic, you can still utilize SwiftUI for the display. You’ll implement what’s known as a Container View Controller. Essentially you take a view controller and make it a child of another view controller.

Receiving the Notification

When a notification arrives, iOS will call the didReceive(_:) method. Replace the contents of the method with the following:

MKMapSnapshotter

The solution is to use an MKMapSnapshotter. Given an MKCoordinateRegion, iOS will take a picture of the map for that region and provide it to your code as a UIImage. There are two methods you can use:

Setting the Entry Point

Remember that the default template expects to display a storyboard. Edit the Info.plist file, the expand the NSExtension key. You will see a key labeled NSExtensionMainStoryboard. Replace the key with NSExtensionPrincipalClass, then replace the value with $(PRODUCT_MODULE_NAME).NotificationViewController. Using the NSExtensionPrincipalClass key lets iOS know how to start your custom UI as you no longer have a storyboard to load.

Resizing the Initial View

If you watch really closely while your custom UI comes into place, you’ll probably notice that it might start a bit too big and then shrink down to the proper size. Apple, without explaining why, implemented the initial height of the view as a percentage of the width, instead of letting you specify a specific size.

Accepting Text Input

At times, you may want to allow your users to type some text in response to a push notification. With the previous map push, people may want to tell you how jealous they are that you’re there or the awesome things they saw last time they went themselves. Or, in your spy app, you might want to request additional information about your target.

Changing Actions

It’s also possible to modify the action buttons dynamically inside of your Notification Content Extension. If you’re sending a social media notification, for example, you may want to provide a button to let the end-user “like” your content. Once you’ve tapped the “Like” button, it only makes sense to now provide an “Unlike” button in its place. In the case of your spy app, you’ll add “Accept” and “Cancel” buttons, to accept your next target and cancel the mission if anything goes wrong.

Attachments

If your project also includes a Service Notification Extension, it will be executed before your Notification Content Extension. A frequent reason you’d have both extensions is that the former will download an attachment that the latter wants to use. It’s not enough to just know where your mission’s target is. You also need to know what they look like; that’s why you’ll add a small image of your target’s headshot to your notification.

Video Attachments

Things get more complicated when your attachment is a video file, however. While this is out-of-scope for your spy app, it’s still a valuable feature to know about.

Custom User Input

While action buttons and the keyboard are great, sometimes you really just want your own custom interface for user input – a grid of buttons, sliders, etc…

Adding a Payment Action

Agents need to get paid! You’ll add a slider that the agents can use to select how much they want to get paid for the job. Head back into your app’s PushNotifications.swift file and add a new case to the ActionIdentifier enum:

The First Responder

Remember way back when you first learned iOS programming, there was that pesky responder chain that never made much sense? Well, it’s finally time to do something useful with it!

The User Input

If you become the first responder, iOS will expect you to return a view via the inputView property that contains your custom user interaction view. The download materials for this chapter includes a PaymentView for you that will display a slider for selecting payments. Drag the PaymentView.swift file from the projects folder into the Custom UI group in Xcode. Make sure Copy items if needed is checked, and also that the Custom UI target is checked.

Hiding Default Content

If you’re creating a custom UI, odds are that you’re already presenting the title and body of the notification somewhere in your UI. If that’s the case, you can tell iOS to not present that default data under your view by editing the content extension’s Info.plist. Expand the NSExtension property again. This time, under NSExtensionAttributes, add a new Boolean key called UNNotificationExtensionDefaultContentHidden and set its value to YES.

Interactive UI

If you want to support interactive touches on your custom user interface, you need to edit the Info.plist of your extension and add the UNNotificationExtensionUserInteractionEnabled attribute key with a value of YES inside NSExtensionAttributes.

Launching the App

Depending on the content of your UI, it may make sense to have a button tap launch your app. This is as simple as calling a single method:

Dismissing the UI

Similarly to being able to launch your app, you can also dismiss the UI based on a button tap. As usual, you’ll want to call a method on the extensionContext:

Debugging

Debugging a UI extension works almost the same as any other Xcode project. However, because it’s a target and not an app, you have to take a few extra steps.

Print With Breakpoints

Because your custom interface runs as a separate process, you will not see any print statements that you place in your code. Instead, you’ll need to make use of Xcode breakpoints.

Key Points

  • You can customize the look of a push notification; custom interfaces are implemented as separate targets in your Xcode project, just like the service extension.
  • Custom interfaces are triggered by specifying a category and every custom UI must have its own unique category identifier.
  • There are a number of customizations you can make such as allowing your user to respond to a push notification with text, changing action buttons, allowing attachements and tailoring your interface for user input like payment actions. You can also hide default content and create an interactive UI. All of these features will enhance your user experience and make your app really stand out.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now