Documentation Index
Fetch the complete documentation index at: https://mintlify.com/EvanBacon/expo-apple-targets/llms.txt
Use this file to discover all available pages before exploring further.
Notification extensions enhance push notifications with rich content, media attachments, and custom actions.
Extension types
Two notification extension types are available:
Notification Content
Custom UI for displaying rich notification content
Notification Service
Modify notification payloads before delivery
Notification Content Extension
Displays custom UI when the user expands a notification.
Extension point
| Property | Value |
|---|
| Type | notification-content |
| Extension Point | com.apple.usernotifications.content-extension |
| Frameworks | UserNotifications, UserNotificationsUI |
Creating a content extension
npx create-target notification-content
Implementation
import UserNotifications
import UserNotificationsUI
class NotificationViewController: UIViewController, UNNotificationContentExtension {
@IBOutlet var label: UILabel?
func didReceive(_ notification: UNNotification) {
// Extract data from notification
let content = notification.request.content
label?.text = content.body
// Access custom payload
if let customData = content.userInfo["customData"] as? String {
// Use custom data
}
}
// Handle notification actions
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
if response.actionIdentifier == "LIKE_ACTION" {
// Handle like action
completion(.dismissAndForwardAction)
} else {
completion(.dismiss)
}
}
}
In Info.plist, specify which notification categories use this extension:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionCategory</key>
<string>myNotificationCategory</string>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>1.0</real>
</dict>
</dict>
Send a notification with this category:
{
"aps": {
"alert": "New message",
"category": "myNotificationCategory"
},
"customData": "value"
}
Notification Service Extension
Modifies notification payloads before they’re delivered to the user. Common uses:
- Download and attach images/videos
- Decrypt encrypted messages
- Update notification content
- Track delivery
Extension point
| Property | Value |
|---|
| Type | notification-service |
| Extension Point | com.apple.usernotifications.service |
| Frameworks | UserNotifications |
Creating a service extension
npx create-target notification-service
Implementation
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
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Download and attach an image
if let imageURLString = request.content.userInfo["image_url"] as? String,
let imageURL = URL(string: imageURLString),
let imageData = try? Data(contentsOf: imageURL),
let attachment = self.createAttachment(data: imageData, identifier: "image") {
bestAttemptContent.attachments = [attachment]
}
// Decrypt content if needed
if let encryptedBody = request.content.userInfo["encrypted_body"] as? String {
bestAttemptContent.body = decrypt(encryptedBody)
}
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called when extension is about to be terminated
// Deliver the best attempt content
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
private func createAttachment(data: Data, identifier: String) -> UNNotificationAttachment? {
let fileManager = FileManager.default
let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
let tmpSubFolderURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectory(at: tmpSubFolderURL, withIntermediateDirectories: true, attributes: nil)
let fileURL = tmpSubFolderURL.appendingPathComponent(identifier)
try data.write(to: fileURL)
let attachment = try UNNotificationAttachment(identifier: identifier, url: fileURL, options: nil)
return attachment
} catch {
print("Error creating attachment: \(error)")
}
return nil
}
private func decrypt(_ encrypted: String) -> String {
// Implement your decryption logic
return encrypted
}
}
Push notification payload:
{
"aps": {
"alert": "Check out this photo!",
"mutable-content": 1
},
"image_url": "https://example.com/photo.jpg"
}
The mutable-content: 1 flag is required for the service extension to run.
Interactive notifications
Define custom notification actions in your main app:
import UserNotifications
func registerNotificationActions() {
let likeAction = UNNotificationAction(
identifier: "LIKE_ACTION",
title: "Like",
options: [.foreground]
)
let commentAction = UNNotificationAction(
identifier: "COMMENT_ACTION",
title: "Comment",
options: [.foreground]
)
let category = UNNotificationCategory(
identifier: "myNotificationCategory",
actions: [likeAction, commentAction],
intentIdentifiers: [],
options: [.customDismissAction]
)
UNUserNotificationCenter.current().setNotificationCategories([category])
}
Supported attachment types:
| Type | Extensions |
|---|
| Images | .jpg, .jpeg, .png, .gif |
| Audio | .mp3, .wav, .aiff |
| Video | .mp4, .m4v, .mov |
Size limits:
- Images: 10 MB
- Audio: 5 MB
- Video: 50 MB
Best practices
- Use URLSession for downloads (supports background)
- Set appropriate timeouts
- Handle network failures gracefully
- Cache downloaded media when possible
- Always implement
serviceExtensionTimeWillExpire()
- Deliver best-attempt content even on failure
- Log errors for debugging
- Test with poor network conditions
- Validate image URLs
- Implement encryption for sensitive data
- Don’t store sensitive data in notifications
- Use App Groups for secure data sharing
Troubleshooting
- Verify
mutable-content: 1 in notification payload
- Check notification category matches Info.plist
- Ensure extension is properly code signed
- Test on a real device (not simulator for remote notifications)
- Verify image URL is accessible
- Check image file size is under 10 MB
- Ensure attachment identifier is unique
- Verify file extension is supported
Learn more
Notifications guide
Detailed notification guide
Target config
Configure notification extensions
Complete target list
All extension types
Apple documentation
Official UserNotifications documentation