iOS Swift推送通知:APP打开时不生效,关闭时正常,如何解决?
Hey there! Let's troubleshoot why your push notifications aren't showing up when your iOS app is open—this is a super common pitfall with remote pushes, and it's usually easy to fix once you know the right spots to check.
First, let's break down how iOS handles push notifications:
- When your app is closed or in the background, iOS automatically displays the notification for you.
- When your app is in the foreground, iOS hands the notification off to your app instead of showing it directly—you have to explicitly tell the system to display it.
The Fix: Implement the Foreground Notification Delegate Method
The key missing piece is probably the willPresent delegate method from UNUserNotificationCenterDelegate. This method fires when a push arrives while your app is active, and you need to call the completion handler with presentation options to make the notification appear.
Here's how to set this up properly, depending on your app structure:
1. For UIKit Apps (AppDelegate-based)
Make sure your AppDelegate conforms to UNUserNotificationCenterDelegate, sets itself as the delegate, and implements the required method:
import UIKit import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Set up notification center delegate UNUserNotificationCenter.current().delegate = self // Request necessary notification permissions let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in if let error = error { print("Notification auth error: \(error.localizedDescription)") } } // Register for remote pushes application.registerForRemoteNotifications() return true } // MARK: - UNUserNotificationCenterDelegate // This is the critical method for foreground notifications func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // Tell iOS to show the notification as a banner, play sound, and update the badge completionHandler([.banner, .sound, .badge]) } // Handle notification taps (works for foreground/background/closed) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // Add your custom tap handling logic here print("User tapped notification: \(response.notification.request.content.body)") completionHandler() } // Handle device token registration func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() print("Device Token: \(tokenString)") // Send this token to your backend } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("Failed to register for remote pushes: \(error.localizedDescription)") } }
2. For SwiftUI Apps (App Struct-based)
If you're using SwiftUI's @main app structure, use UIApplicationDelegateAdaptor to hook up an AppDelegate with the same logic:
import SwiftUI import UserNotifications @main struct YourAppName: App { // Hook up the AppDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in if let error = error { print("Auth error: \(error)") } } application.registerForRemoteNotifications() return true } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.banner, .sound, .badge]) } // Add the other delegate methods (tap handling, token registration) as needed }
Key Checks to Verify
- Delegate is Set: Make sure
UNUserNotificationCenter.current().delegate = selfis called early (like indidFinishLaunchingWithOptions), and the delegate object (AppDelegate) isn't deallocated. - Permissions Granted: Confirm your app has been granted alert/sound/badge permissions (check Settings > Your App > Notifications).
- Payload is Correct: Your push payload should include an
alertfield (for visible content). If you're sending silent pushes, you'll needcontent-available: 1, but for visible notifications,alertis required. - Debug the Delegate Method: Add a
printstatement insidewillPresentto confirm it's being called when a push arrives while the app is open. If it's not firing, double-check your delegate setup.
Once you implement this, your app should display notifications whether it's open, in the background, or completely closed.
内容的提问来源于stack exchange,提问作者user9551058

