Push Notifications & UserNotifications
Requesting Permission
import UserNotifications
func requestNotificationPermission() async -> Bool {
let center = UNUserNotificationCenter.current()
do {
return try await center.requestAuthorization(options: [.alert, .sound, .badge])
} catch {
return false
}
}
// Check current status
func notificationStatus() async -> UNAuthorizationStatus {
await UNUserNotificationCenter.current().notificationSettings().authorizationStatus
}
Scheduling Local Notifications
func scheduleReminder(title: String, body: String, at date: Date) async throws {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
content.badge = 1
let components = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger
)
try await UNUserNotificationCenter.current().add(request)
}
// Interval trigger (e.g., 10 seconds from now)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
// Location trigger
let region = CLCircularRegion(center: coordinate, radius: 100, identifier: "office")
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
Registering for Remote Notifications
// AppDelegate or @UIApplicationDelegateAdaptor
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02x", $0) }.joined()
Task { await sendTokenToServer(token) }
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("APNs registration failed: \(error)")
}
}
// In SwiftUI lifecycle
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup { ContentView() }
.task { UIApplication.shared.registerForRemoteNotifications() }
}
}
Handling Notifications
extension AppDelegate: UNUserNotificationCenterDelegate {
// Notification received while app is in foreground
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.banner, .sound, .badge])
}
// User tapped notification
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
handleNotificationAction(response.actionIdentifier, userInfo: userInfo)
completionHandler()
}
}
Rich Notifications with Attachments
// Add image to notification
func scheduleRichNotification(imageURL: URL) async throws {
let content = UNMutableNotificationContent()
content.title = "New Photo"
content.body = "Someone shared a photo with you"
// Download and attach image
let (tempURL, _) = try await URLSession.shared.download(from: imageURL)
let attachment = try UNNotificationAttachment(
identifier: "image",
url: tempURL,
options: [UNNotificationAttachmentOptionsTypeHintKey: "public.jpeg"]
)
content.attachments = [attachment]
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
try await UNUserNotificationCenter.current().add(request)
}
Notification Categories and Actions
// Register categories at launch
let acceptAction = UNNotificationAction(identifier: "ACCEPT", title: "Accept", options: .foreground)
let declineAction = UNNotificationAction(identifier: "DECLINE", title: "Decline", options: .destructive)
let inviteCategory = UNNotificationCategory(
identifier: "INVITE",
actions: [acceptAction, declineAction],
intentIdentifiers: [],
options: .customDismissAction
)
UNUserNotificationCenter.current().setNotificationCategories([inviteCategory])
// Set category in notification payload (APNs or local)
content.categoryIdentifier = "INVITE"
Common Anti-Patterns
- Not requesting permission before registering — always call
requestAuthorization first
- Storing device tokens as String in CoreData — tokens change; always re-register and update server
- Not handling
didFailToRegisterForRemoteNotifications — APNs fails in simulators without entitlement
- No silent notification handling — add
content-available: 1 in APNs payload for background refresh
- Badge count not cleared — clear badge with
UNUserNotificationCenter.current().setBadgeCount(0)