Notification Extension is an iOS mechanism that allows modifying the content and appearance of push notifications before they are displayed to the user. Extensions run on the operating system side in a separate process: UNNotificationServiceExtension processes incoming content, while UNNotificationContentExtension manages the interface. According to Apple Developer Documentation, 2026, the Service Extension gets up to 30 seconds to perform tasks before the notification is displayed. This limitation is critical when downloading attachments or decrypting data.
Key Takeaways
Notification Extension is an iOS software component that extends the standard behavior of push notifications by adding the ability to modify content and customize the interface. Unlike regular notifications that are displayed by the system in a standard format, extensions allow the developer to influence the content before display and create a unique user interface.
Apple provides two types of Notification Extension for different purposes: UNNotificationServiceExtension handles incoming payload processing, while UNNotificationContentExtension manages display. Service Extension runs before the notification is shown and has a limited execution time — up to 30 seconds according to Apple Developer Documentation. Content Extension activates after the user interacts with the notification and displays a custom view.
A standard iOS push notification is displayed by the system automatically based on the alert, title, and subtitle fields from the JSON payload. Notification Extension intercepts control: Service Extension receives the raw payload, modifies it, and passes it to the system for display. Content Extension replaces the standard banner with a custom interface featuring any control elements.
When the device receives a push notification with the mutable-content: 1 key, the system launches the Service Extension in a separate process. The extension process is isolated from the main application and has its own sandbox with a 50 MB memory limit. After processing completes, the extension calls the completion handler, passing the modified UNNotificationContent to the system for display.
UNNotificationServiceExtension is the primary tool for programmatically modifying push notifications before they are shown to the user. The extension activates automatically when a notification is received whose payload has the mutable-content: 1 flag set. Within 30 seconds, the extension can download a media attachment, change text, decrypt encrypted data, or enrich content.
The extension implements two key methods from the UNNotificationServiceExtension protocol. The didReceive method receives the incoming request with the raw UNNotificationRequest and allows modification through the completion handler with a new UNNotificationContent. The serviceExtensionTimeWillExpire method is called by the system one second before the timeout — in it you must complete processing and pass the current (possibly partial) result.
Consider a scenario where the server sends a push with an image URL in a custom field. Service Extension downloads this image over the network, creates a UNNotificationAttachment, and adds it to the content. UNNotificationAttachment accepts a local image, video, or audio file and automatically copies it to the extension's sandbox. After creating the attachment, the extension passes the updated content to the system.
class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void) {
let userInfo = request.content.userInfo
guard let imageURL = URL(string: userInfo["image-url"] as! String)
else { contentHandler(request.content); return }
let attachment = try! UNNotificationAttachment(
identifier: "image", url: imageURL,
options: [UNNotificationAttachmentOptionsTypeHintKey: "jpg"])
let modifiedContent = request.content.mutableCopy()
as! UNMutableNotificationContent
modifiedContent.attachments = [attachment]
contentHandler(modifiedContent)
}
override func serviceExtensionTimeWillExpire() {
contentHandler?(bestAttemptContent ?? request.content)
}
}
When notifications with the same attachments are sent frequently, it is recommended to cache downloaded files on the device. FileManager provides access to the extension's cache directory, which persists between launches. This reduces processing time for subsequent notifications and lowers network load. According to Apple, caching attachments can reduce processing time to 2–5 seconds instead of a full download.
UNNotificationContentExtension allows replacing the standard notification banner with a custom interface created in Interface Builder or SwiftUI. The extension activates when the user performs an action on the notification: tapping, swiping down, or 3D Touch. Content Extension receives content already processed by Service Extension and displays it in a custom view.
Each Content Extension is linked to one or more notification categories through Info.plist. The category is defined on the server by the category field in the APNS payload. The system automatically selects the appropriate extension based on the category of the received notification. The interface is built via storyboard using standard UIKit components or SwiftUI View.
Content Extension supports custom buttons and tap handlers defined in UNNotificationAction. UNNotificationAction is created at the category registration stage and can support text input via UNTextInputNotificationAction. When the user presses a button, the extension receives the didReceive callback with the action identifier and can execute the corresponding logic — open a URL, send a request to the server, or update the interface.
Content Extension runs in an isolated process with its own run loop and a memory limit of approximately 50 MB. Extension performance is critical because the system watchdog terminates the process when limits are exceeded. It is recommended to avoid heavy computations, loading large images, and lengthy network requests inside Content Extension.
UNNotificationAttachment is an object that adds a media file to a push notification: image, video, audio, or GIF. The attachment is created from a local file URL that must reside in the extension's sandbox. The maximum attachment size must not exceed 10 MB, otherwise the system will reject the attachment upon creation.
Apple supports a limited set of formats for media attachments. Images — JPEG, PNG, GIF (including animated), TIFF. Video — MPEG, MP4, MOV with a maximum duration of 30 seconds. Audio — MP3, AAC, WAV, CAF. For each format, you can specify the type via UNNotificationAttachmentOptionsTypeHintKey, which helps the system process the file correctly.
Since the push payload only contains a URL, not the file itself, downloading the attachment must be done inside Service Extension. Maximum download time is limited to 30 seconds, so it is recommended to use URLSession with minimal settings and disable downloading on weak signal. If the attachment does not load in time, the notification is displayed without media — this is the default system behavior.
Let's walk through a complete Notification Extension example that downloads an image, saves it, and adds it to the notification. NotificationService inherits from UNNotificationServiceExtension and overrides the didReceive method. The example shows handling of optional fields, attachment creation, and calling the completion handler with modified content.
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
self.bestAttemptContent =
(request.content.mutableCopy()
as! UNMutableNotificationContent)
guard let attachmentURLString =
bestAttemptContent?.userInfo["attachment-url"] as? String,
let url = URL(string: attachmentURLString)
else {
contentHandler(request.content)
return
}
URLSession.shared.downloadTask(with: url) {
[weak self] localURL, _, error in
guard let localURL = localURL, error == nil
else {
contentHandler(request.content)
return
}
let attachment = try! UNNotificationAttachment(
identifier: "media", url: localURL)
self?.bestAttemptContent?.attachments = [attachment]
contentHandler(self?.bestAttemptContent
?? request.content)
}.resume()
}
override func serviceExtensionTimeWillExpire() {
if let content = bestAttemptContent {
contentHandler?(content)
}
}
}
For the extension to work correctly, you need to register notification categories in Info.plist. The NSExtensionPointIdentifier key is set to com.apple.usernotifications.service for Service Extension or com.apple.usernotifications.content for Content Extension. Categories are defined in AppDelegate at app launch through UNUserNotificationCenter, and the extension only activates for notifications with a matching category in the payload.
Frequently Asked Questions
Service Extension modifies content before the notification is displayed — adds media, changes text, decrypts data. Content Extension replaces the notification interface with a custom one after user interaction. Service Extension works before display, Content Extension works after.
The system allocates 30 seconds for code execution in didReceive. If processing is not completed within this time, serviceExtensionTimeWillExpire is called, where you need to pass the current result. It is recommended to stay within 10–15 seconds accounting for attachment downloads.
Yes, SwiftUI is supported in Content Extension starting from iOS 16. The View is wrapped in a UIHostingController and added to the storyboard. However, due to memory limits, it is recommended to use SwiftUI only for simple interfaces with a minimal number of elements.
In case of download error or timeout, simply call the completion handler with the original content without an attachment. The system will display the notification in standard form without media. Log errors via OSLog for diagnostics, but do not block notification display.
The extension runs in an isolated process with a limit of approximately 50 MB. When exceeded, the system kills the process via watchdog. Avoid loading large files, storing images in memory, and leaks when working with URLSession.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also