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
swiftimport 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
swiftclass 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
swiftclass 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
swiftclass 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
swiftextension 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
swiftclass 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
swiftclass 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
swiftclass 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
swiftextension 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!