Skip to content
All essays
iOSMarch 27, 202513 min

Push Notifications in Swift: Complete Implementation Guide

Implement push notifications in iOS apps. APNs, local notifications, and best practices.

Ü
Ümit Uz
Mobile & Full Stack Developer

Push notifications are essential for engaging users. Learn to implement both remote and local notifications in iOS apps.

Setup and Configuration

Enable Capabilities

  1. 1Go to Project Settings > Signing & Capabilities
  2. 2Add "Push Notifications" capability
  3. 3Add "Background Modes" capability
  4. 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

  1. 1Permission: Request permission at the right time
  2. 2Personalization: Send personalized notifications
  3. 3Timing: Respect user's time zone
  4. 4Content: Keep messages clear and concise
  5. 5Actions: Provide relevant actions
  6. 6Testing: Test thoroughly on real devices
  7. 7Opt-out: Easy opt-out option
  8. 8Analytics: Track notification performance

Push notifications, when done right, significantly improve user engagement!

Next essay
In-App Purchases with StoreKit: Complete Guide