You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:44:56