iOS推送通知触发指定视图显示失败,求代码排查方案
问题描述
我想在AppDelegate中通过协议实现两个场景的视图展示:
- App处于前台时收到推送通知
- 用户点击推送通知打开App
但代码完全没生效——收到推送时目标视图弹不出来,printLog("showRequestView functionnnn")也没打印。
相关代码
AppDelegate代码
import Firebase import FirebaseAppCheck import FirebaseCore import FirebaseFirestore import FirebaseMessaging import FirebaseStorage import GoogleMaps import IQKeyboardManagerSwift import UserNotifications protocol AppDelegateProtocol { func showRequestView() } class AppDelegate: UIResponder, UIApplicationDelegate { let googleApiKey = Constants.googleApiKey var gcmMessageIDKey = "gcm.message_id" var window: UIWindow? var delegate: AppDelegateProtocol? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GMSServices.provideAPIKey(googleApiKey) let providerFactory = AppCheckDebugProviderFactory() AppCheck.setAppCheckProviderFactory(providerFactory) FirebaseApp.configure() Messaging.messaging().delegate = self Messaging.messaging().isAutoInitEnabled = true IQKeyboardManager.shared.enable = true UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: { _, _ in } ) application.registerForRemoteNotifications() return true } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { printLog(userInfo) delegate?.showRequestView() } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("Unable to register for remote notifications: \(error.localizedDescription)") } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { print("APNs token retrieved: \(deviceToken)") } } extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions { printLog("will present") delegate?.showRequestView() let userInfo = notification.request.content.userInfo if let messageID = userInfo[gcmMessageIDKey] { printLog("Message ID: \(messageID)") printLog("User info: \(userInfo)") } return [[.badge, .sound]] } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { printLog("did Receive") let userInfo = response.notification.request.content.userInfo if let messageID = userInfo[gcmMessageIDKey] { printLog("this is did Receive") printLog("Message ID: \(messageID)") } printLog(userInfo) } } extension AppDelegate: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { print("Firebase registration token: \(String(describing: fcmToken))") Constants.fcmTokenValue = fcmToken ?? "" let dataDict: [String: String] = ["token": fcmToken ?? ""] NotificationCenter.default.post( name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict ) } }
ViewController代码
class TransportRequestViewController: UIViewController,AppDelegateProtocol { let appDelegate = AppDelegate() override func viewDidLoad() { super.viewDidLoad() appDelegate.delegate = self printLog("transportRequest VIEW DID LOAD!") } func showRequestView() { printLog("showRequestView functionnnn") let storyboard = UIStoryboard(name: "transportRequest", bundle: .main) let vc = storyboard.instantiateViewController(identifier: "transportRequest") as! TransportRequestViewController present(vc, animated: true, completion: nil) } }
问题排查与修复
1. 核心问题:AppDelegate实例不匹配
你在TransportRequestViewController里新建了一个AppDelegate()实例,但系统实际运行的是UIApplication管理的单例实例,你设置的delegate根本没关联到真正工作的那个AppDelegate上,所以推送回调里的delegate?.showRequestView()等于空调用,完全没触发。
修复代码:
把ViewController里的let appDelegate = AppDelegate()替换成获取系统单例:
class TransportRequestViewController: UIViewController,AppDelegateProtocol { // 替换原来的实例创建代码 private var appDelegate: AppDelegate? override func viewDidLoad() { super.viewDidLoad() // 获取系统真正在用的AppDelegate实例 appDelegate = UIApplication.shared.delegate as? AppDelegate appDelegate?.delegate = self printLog("transportRequest VIEW DID LOAD!") } // 补充销毁时的清理,避免野指针 deinit { appDelegate?.delegate = nil } // ... 其他代码 }
2. 点击推送时未触发视图展示
在UNUserNotificationCenterDelegate的didReceive response方法里,你没有调用delegate?.showRequestView(),所以用户点击推送打开App时,不会执行视图展示逻辑。
修复代码:
在AppDelegate的该方法里添加触发代码:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { printLog("did Receive") // 新增这一行 delegate?.showRequestView() let userInfo = response.notification.request.content.userInfo if let messageID = userInfo[gcmMessageIDKey] { printLog("this is did Receive") printLog("Message ID: \(messageID)") } printLog(userInfo) }
3. 视图展示逻辑错误
你在showRequestView里present了当前VC的新实例,这会导致重复弹出同一个页面,而且如果当前VC不是最上层控制器,直接调用present可能无效。
修复代码:
先判断当前是否已经是目标页面,再从最上层控制器进行展示:
func showRequestView() { printLog("showRequestView functionnnn") // 避免重复弹出同一个页面 if self is TransportRequestViewController { return } let storyboard = UIStoryboard(name: "transportRequest", bundle: .main) let vc = storyboard.instantiateViewController(identifier: "transportRequest") as! TransportRequestViewController // 获取当前最上层的可见控制器 guard let topVC = UIApplication.shared.keyWindow?.rootViewController?.topMostViewController() else { return } topVC.present(vc, animated: true, completion: nil) } // 新增扩展,用来获取最上层控制器 extension UIViewController { func topMostViewController() -> UIViewController { if let presented = presentedViewController { return presented.topMostViewController() } if let nav = self as? UINavigationController { return nav.visibleViewController?.topMostViewController() ?? nav } if let tab = self as? UITabBarController { return tab.selectedViewController?.topMostViewController() ?? tab } return self } }
内容的提问来源于stack exchange,提问作者mohammadgdz
相关产品推荐
相关产品推荐

