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

Swift 4+Firebase实现Google登录后无法推送视图控制器的问题咨询

解决Swift 4 + Firebase Google登录后跳转视图控制器的问题

我来帮你解决这个问题!你遇到的核心问题是AppDelegate里没有navigationController的引用,没法直接执行push操作,下面给你几个可行的解决方案,其中第一种是最符合架构逻辑的:

方案一:将Google登录代理移至托管登录按钮的视图控制器

这是最推荐的做法,把登录相关的逻辑集中在负责登录UI的视图控制器里,符合MVC的单一职责原则,也能直接拿到navigationController的引用。

步骤说明:

  1. 在你的登录视图控制器(比如LoginViewController)中实现GIDSignInDelegate协议
  2. 在该控制器的viewDidLoad里设置Google登录的代理为当前控制器
  3. 在didSignInFor方法中完成Firebase认证后,直接调用navigationController的push方法

代码示例:

class LoginViewController: UIViewController, GIDSignInDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 设置Google登录代理为当前控制器
        GIDSignIn.sharedInstance().delegate = self
        // 如果没有在AppDelegate里设置clientID,这里补充设置
        GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
    }

    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
        // 处理登录错误
        if let error = error {
            print("Google登录失败:\(error.localizedDescription)")
            return
        }
        
        // 获取用户认证信息并转换为Firebase凭证
        guard let authentication = user.authentication else { 
            print("无法获取用户认证信息")
            return 
        }
        let credential = GoogleAuthProvider.credential(
            withIDToken: authentication.idToken, 
            accessToken: authentication.accessToken
        )
        
        // 完成Firebase用户认证
        Auth.auth().signIn(with: credential) { [weak self] authResult, error in
            guard let strongSelf = self else { return }
            
            if let error = error {
                print("Firebase认证失败:\(error.localizedDescription)")
                return
            }
            
            // 认证成功,跳转目标视图控制器
            let browserVC = BrowserViewController()
            strongSelf.navigationController?.pushViewController(browserVC, animated: true)
        }
    }
}

方案二:通过通知中心实现跨组件通信

如果不想移动代理逻辑到登录控制器,可以用NotificationCenter在AppDelegate和视图控制器之间传递登录成功的信号。

步骤说明:

  1. 在AppDelegate的didSignInFor方法中,完成Firebase认证后发送自定义通知
  2. 在登录视图控制器中监听该通知,收到后执行跳转操作

代码示例:

AppDelegate中的代码:

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
    // ... 省略错误处理和Firebase认证代码 ...
    
    Auth.auth().signIn(with: credential) { authResult, error in
        if let error = error {
            print("Firebase认证失败:\(error.localizedDescription)")
            return
        }
        // 发送登录成功的通知
        NotificationCenter.default.post(
            name: NSNotification.Name(rawValue: "GoogleLoginSuccess"), 
            object: nil
        )
    }
}

登录视图控制器中的代码:

class LoginViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 监听登录成功通知
        NotificationCenter.default.addObserver(
            self, 
            selector: #selector(handleLoginSuccess), 
            name: NSNotification.Name(rawValue: "GoogleLoginSuccess"), 
            object: nil
        )
    }
    
    @objc private func handleLoginSuccess() {
        // 执行跳转操作
        let browserVC = BrowserViewController()
        navigationController?.pushViewController(browserVC, animated: true)
    }
    
    deinit {
        // 移除通知监听,避免内存泄漏
        NotificationCenter.default.removeObserver(self)
    }
}

方案三:在AppDelegate中直接获取根视图控制器跳转

如果一定要在AppDelegate里处理跳转,可以通过获取当前窗口的根视图控制器来实现:

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
    // ... 省略错误处理和Firebase认证代码 ...
    
    Auth.auth().signIn(with: credential) { authResult, error in
        if let error = error {
            print("Firebase认证失败:\(error.localizedDescription)")
            return
        }
        
        // 获取当前应用的主窗口
        guard let window = UIApplication.shared.windows.first else { return }
        
        // 尝试获取根导航控制器并跳转
        if let navController = window.rootViewController as? UINavigationController {
            let browserVC = BrowserViewController()
            navController.pushViewController(browserVC, animated: true)
        } else {
            // 如果根控制器不是导航控制器,先包装成导航控制器再设置为根
            let browserVC = BrowserViewController()
            let navController = UINavigationController(rootViewController: browserVC)
            window.rootViewController = navController
            window.makeKeyAndVisible()
        }
    }
}

关于是否移动didSignInFor方法的疑问

非常推荐你把didSignInFor方法移到托管登录按钮的视图控制器,这样登录相关的UI和逻辑都集中在同一个组件里,代码更易维护,也符合iOS开发的MVC架构设计原则,避免把业务逻辑堆积在AppDelegate中。

内容的提问来源于stack exchange,提问作者JSD

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:06:54