Use when needing APNs HTTP/2 transport details, JWT authentication setup, payload key reference, UNUserNotificationCenter API, notification category/action registration, service extension lifecycle, local notification triggers, Live Activity push headers, or broadcast push format. Covers complete push notification API surface.
Comprehensive API reference for APNs HTTP/2 transport, UserNotifications framework, and push-driven features including Live Activities and broadcast push.
{
"aps": {
"alert": {
"title": "Package Delivered",
"body": "Your order has been delivered to the front door"
},
"interruption-level": "time-sensitive",
"category": "DELIVERY",
"sound": "default"
},
"order-id": "12345"
}
let center = UNUserNotificationCenter.current()
let granted = try await center.requestAuthorization(options: [.alert, .sound, .badge])
if granted {
await MainActor.run {
UIApplication.shared.registerForRemoteNotifications()
}
}
Check Settings
swift
let settings = await center.notificationSettings()
switch settings.authorizationStatus {
case .authorized: break
case .denied:
// Direct user to Settings
case .provisional:
// Upgrade to full authorization
case .notDetermined:
// Request authorization
case .ephemeral:
// App Clip — temporary
@unknown default: break
}
Delegate Methods
swift
// Foreground presentation — called when notification arrives while app is active
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification) async
-> UNNotificationPresentationOptions {
return [.banner, .sound, .badge]
}
// Action response — called when user taps notification or action button
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse) async {
let actionIdentifier = response.actionIdentifier
let userInfo = response.notification.request.content.userInfo
switch actionIdentifier {
case UNNotificationDefaultActionIdentifier:
// User tapped notification body
break
case UNNotificationDismissActionIdentifier:
// User dismissed (requires .customDismissAction on category)
break
default:
// Custom action
break
}
}
// Settings — called when user taps "Configure in App" from notification settings
func userNotificationCenter(_ center: UNUserNotificationCenter,
openSettingsFor notification: UNNotification?) {
// Navigate to in-app notification settings
}
UNNotificationCategory and UNNotificationAction API
Category Registration
swift
let likeAction = UNNotificationAction(
identifier: "LIKE",
title: "Like",
options: []
)
let replyAction = UNTextInputNotificationAction(
identifier: "REPLY",
title: "Reply",
options: [],
textInputButtonTitle: "Send",
textInputPlaceholder: "Type a message..."
)
let deleteAction = UNNotificationAction(
identifier: "DELETE",
title: "Delete",
options: [.destructive, .authenticationRequired]
)
let messageCategory = UNNotificationCategory(
identifier: "MESSAGE",
actions: [likeAction, replyAction, deleteAction],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: "New message",
categorySummaryFormat: "%u more messages",
options: [.customDismissAction]
)
UNUserNotificationCenter.current().setNotificationCategories([messageCategory])
Action Options
Option
Effect
.authenticationRequired
Requires device unlock
.destructive
Red text display
.foreground
Launches app to foreground
Category Options
Option
Effect
.customDismissAction
Fires delegate on dismiss
.allowInCarPlay
Show actions in CarPlay
.hiddenPreviewsShowTitle
Show title when previews hidden
.hiddenPreviewsShowSubtitle
Show subtitle when previews hidden
.allowAnnouncement
Siri can announce (deprecated iOS 15+)
UNNotificationActionIcon (iOS 15+)
swift
let icon = UNNotificationActionIcon(systemImageName: "hand.thumbsup")
let action = UNNotificationAction(
identifier: "LIKE",
title: "Like",
options: [],
icon: icon
)
UNNotificationServiceExtension API
Modifies notification content before display. Runs in a separate extension process.
Lifecycle
Method
Window
Purpose
didReceive(_:withContentHandler:)
~30 seconds
Modify notification content
serviceExtensionTimeWillExpire()
Called at deadline
Deliver best attempt immediately
Implementation
swift
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)
guard let content = bestAttemptContent,
let imageURLString = content.userInfo["image-url"] as? String,
let imageURL = URL(string: imageURLString) else {
contentHandler(request.content)
return
}
// Download and attach image
let task = URLSession.shared.downloadTask(with: imageURL) { url, _, error in
defer { contentHandler(content) }
guard let url = url, error == nil else { return }
let attachment = try? UNNotificationAttachment(
identifier: "image",
url: url,
options: [UNNotificationAttachmentOptionsTypeHintKey: "public.jpeg"]
)
if let attachment = attachment {
content.attachments = [attachment]
}
}
task.resume()
}
override func serviceExtensionTimeWillExpire() {
if let content = bestAttemptContent {
contentHandler?(content)
}
}
}
Supported Attachment Types
Type
Extensions
Max Size
Image
.jpg, .gif, .png
10 MB
Audio
.aif, .wav, .mp3
5 MB
Video
.mp4, .mpeg
50 MB
Payload Requirement
The notification payload must include "mutable-content": 1 in the aps dictionary for the service extension to fire.
Local Notifications API
Trigger Types
Trigger
Use Case
Repeating
UNTimeIntervalNotificationTrigger
After N seconds
Yes (≥60s)
UNCalendarNotificationTrigger
Specific date/time
Yes
UNLocationNotificationTrigger
Enter/exit region
Yes
Time Interval Trigger
swift
let content = UNMutableNotificationContent()
content.title = "Reminder"
content.body = "Time to take a break"
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 300, repeats: false)
let request = UNNotificationRequest(
identifier: "break-reminder",
content: content,
trigger: trigger
)
try await UNUserNotificationCenter.current().add(request)
// Observe push-to-start tokens (iOS 17.2+)
for await token in Activity<GameAttributes>.pushToStartTokenUpdates {
let tokenString = token.map { String(format: "%02x", $0) }.joined()
sendPushToStartTokenToServer(tokenString)
}
Activity Push Token
swift
// Observe activity-specific push tokens
for await tokenData in activity.pushTokenUpdates {
let token = tokenData.map { String(format: "%02x", $0) }.joined()
sendActivityTokenToServer(token, activityId: activity.id)
}
Content-state encoding rule: the system always uses default JSONDecoder — do not use custom encoding strategies in your ActivityAttributes.ContentState.
Broadcast Push API (iOS 18+)
Server-to-many push for Live Activities without tracking individual device tokens.