civil-and-structural-engineering
Implementing Push Notification Actions and Custom Ui in Ios
Table of Contents
Understanding Push Notification Actions in iOS
Push notifications have evolved from simple alert banners to rich interactive messages. In iOS, notification actions allow users to perform specific tasks directly from the notification without launching the main app. This can dramatically improve engagement for features like quick replies, content approval, or status updates. The UserNotifications framework provides all the tools needed to define, register, and handle these actions.
Types of Notification Actions
iOS supports two main types of notification actions: UNNotificationAction and UNTextInputNotificationAction. A standard action displays a button with a title and can be configured to require or not require authentication before performing the action. A text input action adds a text field alongside the button, enabling users to type a reply or add a comment directly from the notification. Both types support a UNNotificationActionOptions bitmask that controls behavior such as foreground launch, destructive styling, or requiring the device to be unlocked.
Defining Actions and Categories
Each action belongs to a category identified by a unique string identifier. Categories group related actions for a specific type of notification. For example, a messaging app might define a “MESSAGE_REPLY” category with a text input action for replying and a “MESSAGE_ARCHIVE” action for marking as read. When a remote push payload includes the category key, iOS displays the actions registered for that category.
Developers create UNNotificationCategory objects with an array of actions and optional UNNotificationCategoryOptions for custom dismissal or preview placement. It’s critical to use consistent category identifiers between your app and your push notification server so the correct actions appear on the device.
Registering Categories with the System
Categories must be registered early in the app lifecycle, typically in application(_:didFinishLaunchingWithOptions:) or shortly after requesting notification authorization. Use UNUserNotificationCenter.current().setNotificationCategories(_:) to pass an array of category objects. The system persists these categories, but you should re-register them on each launch to pick up any updates. Because categories are cached, you can also add new categories without requesting user permission again.
Handling Action Selection in the App Delegate
When a user taps an action button or submits a text input, the system calls userNotificationCenter(_:didReceive:withCompletionHandler:) on your UNUserNotificationCenterDelegate. Inside this method, you can inspect the response’s actionIdentifier to determine which action was used. For text input actions, the userText property on the response object holds the typed text. Always call the completion handler after processing the action to avoid warnings from the system. For background actions, you may need to start a background task to complete network calls.
Tip: Use the
UNNotificationDefaultActionIdentifierconstant to detect when the user taps the notification body itself rather than an action button.
Implementing Custom UI with Notification Content Extensions
Notification Content Extensions let you replace the default notification interface with a completely custom view. This extension runs as a separate process and can display interactive elements such as buttons, sliders, images, or even a map view. When a push notification arrives or when the user long‑presses (3D Touch) or clicks (on devices without Force Touch) the banner, your custom view controller takes over the notification’s expanded presentation.
Setting Up the Content Extension Target
In Xcode, add a new target to your project by selecting File > New > Target… and choosing “Notification Content Extension.” Xcode creates a new folder with a NotificationViewController subclass, a storyboard (or XIB), and an Info.plist with the required extension keys. The extension’s Info.plist must contain a NSExtension dictionary with the key NSExtensionPrincipalClass pointing to your view controller class name and NSExtensionAttributes containing the UNNotificationExtensionCategory array. This array lists the notification categories your extension supports – only push notifications with a matching category value will activate your custom interface.
Designing the Custom Interface
You can design the UI in the provided storyboard or create it entirely in code inside your NotificationViewController. The interface is constrained to the notification’s available height, which can expand dynamically. Use Auto Layout to adapt to different device sizes and notification expansion states. For a rich media notification, add a UIImageView populated later with an image fetched from a URL in the notification payload. Remember that the extension runs in its own sandbox, so you cannot directly access app preferences or the main app’s Core Data store. Instead, use an App Group container to share small data, or rely on the notification’s payload to pass necessary information.
Populating Content with didReceive(_:with:)
The key method to override in your NotificationViewController is didReceive(_:with:). This method is called after the notification is delivered. The first parameter is a UNNotification object containing both the request and the content (including title, subtitle, body, attachments, and custom userInfo dictionary). The second parameter is a UNNotificationContentExtension object that provides completion block methods. Use the notification’s request.content.userInfo to extract custom keys sent in the remote payload, then update your UI elements accordingly. If your custom view needs to fetch additional data (for example, downloading a high‑resolution image), perform that work asynchronously and update the UI once complete.
Performance note: The system sets a memory and CPU limit on extension execution. Avoid heavy processing or large resource downloads that might cause the extension to be terminated.
Handling User Interactions
Because the extension runs in a different process, handling taps on buttons inside the custom view requires explicit communication with the host app. For simple actions (like a “Like” button), you can call extensionContext?.notificationActions and then perform a UNNotificationAction identical to those registered for the category. Alternatively, you can use a UNNotificationContentExtensionMediaPlayPauseButtonType to display a play/pause button for media notifications. For more complex interactions, you may need to open the host app via a URL scheme or through the UNUserNotificationCenter delegate in the main app, passing context via the userInfo payload.
Communicating with the Host App
Use the open(_:completionHandler:) method on the extension context, passing a URL that your main app registers. For example, you can construct a custom URL like yourapp://notification?action=approve¬ificationID=123. When the host app opens, it parses the URL and performs the requested operation. A more modern approach is to rely on UNNotificationAction responses that are automatically forwarded to the main app’s delegate – this avoids the extra overhead of URL opening and keeps the action handling centralized in the delegate method.
Advanced Customization with Notification Service Extensions
While Notification Content Extensions alter the display, Notification Service Extensions modify the content before it is presented. By combining both extensions, you can download images or videos, decrypt encrypted payloads, or mutate the notification text. The service extension runs after the push arrives but before it is shown, giving you a chance to enrich the payload with attachments that the content extension can then display. This pairing is particularly useful for media‑rich notifications like breaking news with an image or a social media post preview.
Best Practices for Push Notification Actions and Custom UI
Relevance and User Experience
Actions should be immediately useful. Avoid cluttering the interface with too many buttons – iOS displays up to four actions per category, but user testing shows that two or three perform best. For custom UI, keep the layout clean and focused on a single task. Use system fonts and colors to maintain familiarity, but don’t shy away from branding elements as long as they respect user expectations.
Performance and Reliability
Notification extensions are short‑lived processes. If your code takes too long to execute (more than a few seconds), the system will terminate the extension and fall back to the default notification UI. Always use asynchronous networking, cache resources where possible, and avoid synchronous operations on the main thread. Test your extension on older devices where memory constraints are tighter.
Testing Across Devices and iOS Versions
Different iOS versions handle notifications slightly differently. For example, iOS 17 introduced new notification grouping behaviors and changed how rich notifications appear on the lock screen. Always test on physical devices (both with and without 3D Touch/Haptic Touch) and use the xcrun simctl command or Xcode’s push notification payload editor in the simulator to mock remote pushes. The UNNotificationContentExtension lifecycle is not triggered in the default push simulator dialog; you must use a real payload or a local notification with the proper category.
Privacy and User Control
Be transparent about what the action does. For destructive actions like “Delete,” use the .destructive option on the UNNotificationActionOptions so iOS displays the action in red. For actions that launch the app, consider whether the user expects to leave their current context. Always provide a way to dismiss the notification without performing any action. If your custom UI collects user input (like a survey), ensure you have obtained appropriate consent and meet App Store guidelines regarding data collection.
Accessibility
Make custom UI fully accessible by using standard UIKit controls, setting proper accessibility labels, and supporting Dynamic Type. Actions that are only accessible via a visual button on the custom view should also be available as a UNNotificationAction so VoiceOver users can interact via the standard action menu. Test your extension with VoiceOver enabled to confirm focus order and speakability of all elements.
Localisation
Notification payloads can include localized strings using the loc-key and loc-args keys. For custom UI, you should localize any static text in your storyboard or code. Provide localized strings for action titles in your app’s Localizable.strings files so that when the remote push uses the actionLocKey, the correct translation appears.
Common Pitfalls and Troubleshooting
Category not matching: The most frequent issue is that the push payload contains a category value that does not match any registered category. Double‑check that the string matches exactly, including case and underscores. Use the UNUserNotificationCenter.current().getNotificationCategories(completionHandler:) to verify what is registered at runtime.
Extension not loaded: If your custom view does not appear, confirm that the UNNotificationExtensionCategory array in the extension’s Info.plist includes the category from the payload. Also ensure the principal class name is correct and that the extension target is included in the build scheme.
Memory crashes: Extensions are killed if they exceed 30 MB of memory. Use Instruments to profile memory usage and reduce image sizes or lazy load content. Consider using smaller thumbnail versions in the notification and deferring full‑size downloads until the app is opened.
Action not firing: If tapping an action does nothing, verify that the UNUserNotificationCenterDelegate is set in the main app (usually in the app delegate). The delegate method must be implemented and the completion handler must be called. For background actions, ensure the app has the appropriate background modes capability if network calls are involved.
UI not updating after content fetch: Always update the UI on the main thread. Use DispatchQueue.main.async inside your network completion handler. Also, call extensionContext?.performNotificationDefaultAction() or the appropriate completion block to let the system know the extension is ready and has finished its UI updates.
Conclusion
Implementing push notification actions and custom user interfaces in iOS transforms static alerts into dynamic, interactive components that drive user engagement. By leveraging the UserNotifications framework’s action system and the flexibility of Notification Content Extensions, developers can create experiences that feel immediate and personalized. The key to success lies in careful planning of categories, efficient extension code, thorough testing across devices, and respect for user privacy and accessibility. When done right, these notifications not only retain users but also provide a seamless path to deeper app interactions without friction.
For further reading, consult Apple’s official documentation on UNNotificationAction, UNNotificationCategory, and the UNNotificationContentExtension guide. A practical walkthrough with sample code is also available in Apple’s Remote Notifications Programming Guide.