Push notifications are essential for engaging users. Learn to implement both remote and local notifications in iOS apps.
Setup and Configuration
Enable Capabilities
- 1Go to Project Settings > Signing & Capabilities
- 2Add "Push Notifications" capability
- 3Add "Background Modes" capability
- 4Enable "Remote notifications"
Request Authorization
swift
import UserNotifications
class NotificationManager: ObservableObject {
func requestAuthorization() async -> Bool {
let center = UNUserNotificationCenter.current()
do {
let granted = try await center.requestAuthorization(
options: [.alert, .sound, .badge]
)
await MainActor.run {
UIApplication.shared.registerForRemoteNotifications()
}
return granted
} catch {
print("Authorization failed: \(error)")
return false
}
}
func getAuthorizationStatus() async -> UNAuthorizationStatus {
let center = UNUserNotificationCenter.current()
let settings = await center.notificationSettings()
return settings.authorizationStatus
}
}Remote Notifications
Register for APNs
swift
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
Task {
let granted = await NotificationManager().requestAuthorization()
print("Authorization granted: \(granted)")
}
return true
}
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print("Device Token: \(token)")
// Send token to your server
Task {
await sendDeviceTokenToServer(token)
}
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}
}Handle Incoming Notifications
swift
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any]
) async -> UIBackgroundFetchResult {
// Handle notification
if let aps = userInfo["aps"] as? [String: Any] {
print("Received notification: \(aps)")
}
return .newData
}
}Local Notifications
Schedule Notification
swift
class NotificationManager {
func scheduleNotification(
title: String,
body: String,
delay: TimeInterval = 5
) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(
timeInterval: delay,
repeats: false
)
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling: \(error)")
}
}
}
func scheduleRepeatingNotification(
title: String,
body: String,
interval: TimeInterval = 60
) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(
timeInterval: interval,
repeats: true
)
let request = UNNotificationRequest(
identifier: "repeating-\(UUID().uuidString)",
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request)
}
}Calendar Trigger
swift
extension NotificationManager {
func scheduleDateNotification(
title: String,
body: String,
date: Date,
repeats: Bool = false
) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let calendar = Calendar.current
let components = calendar.dateComponents(
[.year, .month, .day, .hour, .minute],
from: date
)
let trigger = UNCalendarNotificationTrigger(
dateMatching: components,
repeats: repeats
)
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request)
}
}Rich Notifications
Add Actions
swift
class NotificationManager {
func setupNotificationCategories() {
let acceptAction = UNNotificationAction(
identifier: "ACCEPT_ACTION",
title: "Accept",
options: [.foreground]
)
let declineAction = UNNotificationAction(
identifier: "DECLINE_ACTION",
title: "Decline",
options: [.destructive]
)
let category = UNNotificationCategory(
identifier: "INVITATION_CATEGORY",
actions: [acceptAction, declineAction],
intentIdentifiers: []
)
UNUserNotificationCenter.current()
.setNotificationCategories([category])
}
func sendInteractiveNotification(
title: String,
body: String
) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
content.categoryIdentifier = "INVITATION_CATEGORY"
let trigger = UNTimeIntervalNotificationTrigger(
timeInterval: 1,
repeats: false
)
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request)
}
}Handle Actions
swift
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
switch response.actionIdentifier {
case "ACCEPT_ACTION":
handleAcceptAction(userInfo: userInfo)
case "DECLINE_ACTION":
handleDeclineAction(userInfo: userInfo)
default:
handleDefaultAction(userInfo: userInfo)
}
completionHandler()
}
private func handleAcceptAction(userInfo: [AnyHashable: Any]) {
// Handle accept action
print("Accepted")
}
private func handleDeclineAction(userInfo: [AnyHashable: Any]) {
// Handle decline action
print("Declined")
}
private func handleDefaultAction(userInfo: [AnyHashable: Any]) {
// Handle default tap
print("Default action")
}
}Notification Delegate
Foreground Notifications
swift
class NotificationManager: NSObject, ObservableObject, UNUserNotificationCenterDelegate {
override init() {
super.init()
UNUserNotificationCenter.current().delegate = self
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
// Show notification when app is in foreground
completionHandler([.banner, .sound, .badge])
}
}Background Notifications
Silent Notifications
swift
extension AppDelegate {
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
// Handle silent notification
if let contentAvailable = userInfo["content-available"] as? Int,
contentAvailable == 1 {
// Fetch new data
Task {
await fetchNewData()
completionHandler(.newData)
}
} else {
completionHandler(.noData)
}
}
private func fetchNewData() async {
// Fetch data from server
}
}Best Practices
- 1Permission: Request permission at the right time
- 2Personalization: Send personalized notifications
- 3Timing: Respect user's time zone
- 4Content: Keep messages clear and concise
- 5Actions: Provide relevant actions
- 6Testing: Test thoroughly on real devices
- 7Opt-out: Easy opt-out option
- 8Analytics: Track notification performance
Push notifications, when done right, significantly improve user engagement!