iOS端FCM通知点击跳转指定页面并展示数据异常问题
Got it, let's work through this FCM notification redirect issue together. I’ve tackled this exact scenario before, so here’s a step-by-step solution to get your app navigating to the right screen and displaying the notification data properly:
First, ensure your AppDelegate is set up to intercept notification interactions. You need to assign the UNUserNotificationCenter delegate to yourself—otherwise iOS will default to opening the home screen. Here’s the updated setup:
import UIKit import Firebase import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() // Critical: Set UNUserNotificationCenter delegate to self UNUserNotificationCenter.current().delegate = self Messaging.messaging().delegate = self // Request necessary notification permissions let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: { _, _ in } ) application.registerForRemoteNotifications() // Check if app was launched from a notification (background/killed state) if let notificationData = launchOptions?[.remoteNotification] as? [String: Any] { handleInitialNotificationLaunch(from: notificationData) } return true } // ... keep your existing AppDelegate methods here }
When your app is in the foreground and the user taps a notification, the userNotificationCenter(_:didReceive:withCompletionHandler:) method fires. This is where you’ll override the default behavior and redirect to your target screen:
// MARK: - UNUserNotificationCenterDelegate extension AppDelegate { // Triggered when user taps a notification (foreground/background) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo // Extract your custom payload data (match keys to your FCM server payload) guard let targetScreen = userInfo["target_screen"] as? String, let notificationDetails = userInfo["notification_data"] as? [String: Any] else { // Fallback to home screen if required data is missing navigateToHome() completionHandler() return } // Navigate based on the target screen identifier switch targetScreen { case "order_detail": navigateToOrderDetail(with: notificationDetails) case "message_inbox": navigateToMessageInbox(with: notificationDetails) // Add cases for all your app's target screens default: navigateToHome() } completionHandler() } // Optional: Show notifications as alerts while the app is in foreground func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert, .badge, .sound]) } }
Next, create helper methods to navigate to your target view controllers and inject the notification data. Adjust this code to match your app’s navigation structure (e.g., UINavigationController, UITabBarController):
extension AppDelegate { private func navigateToHome() { guard let window = self.window else { return } let homeVC = HomeViewController() let navController = UINavigationController(rootViewController: homeVC) window.rootViewController = navController window.makeKeyAndVisible() } private func navigateToOrderDetail(with data: [String: Any]) { guard let window = self.window else { return } // Initialize your target view controller let orderDetailVC = OrderDetailViewController() // Pass data to the VC (adjust properties to match your VC's setup) orderDetailVC.orderId = data["order_id"] as? String orderDetailVC.orderStatus = data["status"] as? String // Navigate: Push if inside a navigation stack, or set as root if needed if let currentNav = window.rootViewController as? UINavigationController { currentNav.pushViewController(orderDetailVC, animated: true) } else { let navController = UINavigationController(rootViewController: orderDetailVC) window.rootViewController = navController window.makeKeyAndVisible() } } private func navigateToMessageInbox(with data: [String: Any]) { guard let window = self.window else { return } let messageVC = MessageInboxViewController() messageVC.unreadMessageCount = data["unread_count"] as? Int ?? 0 // If using a tab bar controller, switch to the correct tab first if let tabBar = window.rootViewController as? UITabBarController { tabBar.selectedIndex = 2 // Update to your message tab index if let tabNav = tabBar.selectedViewController as? UINavigationController { tabNav.pushViewController(messageVC, animated: true) } } else { let navController = UINavigationController(rootViewController: messageVC) window.rootViewController = navController window.makeKeyAndVisible() } } // Handle notification taps that launch the app from background/killed state private func handleInitialNotificationLaunch(from userInfo: [String: Any]) { // Wait a short delay to ensure the window is fully initialized DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in guard let targetScreen = userInfo["target_screen"] as? String, let details = userInfo["notification_data"] as? [String: Any] else { return } switch targetScreen { case "order_detail": self?.navigateToOrderDetail(with: details) case "message_inbox": self?.navigateToMessageInbox(with: details) default: self?.navigateToHome() } } } }
Your server’s FCM payload must include the custom fields your app expects to identify the target screen and pass data. Here’s a sample payload format:
{ "to": "YOUR_DEVICE_TOKEN", "notification": { "title": "New Order Alert", "body": "Order #456 has been delivered!" }, "data": { "target_screen": "order_detail", "notification_data": { "order_id": "456", "status": "delivered", "delivery_time": "2024-05-20 14:30" } } }
- Always use the
datafield for custom data: Thenotificationfield is only for display, and custom data won’t be accessible there. - Test all states: Verify navigation works when the app is foreground, background, and fully killed.
- Use weak self in async blocks: Prevent retain cycles in the delayed navigation for initial app launches.
内容的提问来源于stack exchange,提问作者Lahari Areti

