Swift 4+Firebase实现Google登录后无法推送视图控制器的问题咨询
解决Swift 4 + Firebase Google登录后跳转视图控制器的问题
我来帮你解决这个问题!你遇到的核心问题是AppDelegate里没有navigationController的引用,没法直接执行push操作,下面给你几个可行的解决方案,其中第一种是最符合架构逻辑的:
方案一:将Google登录代理移至托管登录按钮的视图控制器
这是最推荐的做法,把登录相关的逻辑集中在负责登录UI的视图控制器里,符合MVC的单一职责原则,也能直接拿到navigationController的引用。
步骤说明:
- 在你的登录视图控制器(比如
LoginViewController)中实现GIDSignInDelegate协议 - 在该控制器的
viewDidLoad里设置Google登录的代理为当前控制器 - 在
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和视图控制器之间传递登录成功的信号。
步骤说明:
- 在
AppDelegate的didSignInFor方法中,完成Firebase认证后发送自定义通知 - 在登录视图控制器中监听该通知,收到后执行跳转操作
代码示例:
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
相关产品推荐
相关产品推荐

