UIKit集成Google登录后如何跳转主界面并持久化登录状态
iOS Google Sign-In 登录流实现方案
问题现状
你在独立登录页集成Google Sign-In后遇到两个问题:
- 登录后主界面以模态形式弹出,用户可轻易返回登录页打乱流程
- 每次应用冷启动都需要重复登录,无法记住登录状态
当前错误实现代码如下:
@IBAction func signIn(_ sender: Any) { let signInConfig = GIDConfiguration.init(clientID: "xyz.apps.googleusercontent.com") GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in guard error == nil else { return } // If sign in succeeded, display the app's main content View. let vc = self.storyboard?.instantiateViewController(withIdentifier: "scanReceiptsViewController") as! ViewController self.navigationController?.pushViewController(vc, animated: true) self.present(vc, animated: true, completion: nil) } }
问题根源是同时调用了pushViewController和present两种冲突的跳转方式,且没有做启动时的登录态持久化判断。
你的Storyboard结构参考:
具体实现步骤
1. 修复登录成功跳转逻辑
不要用push或模态弹出的方式跳主界面,直接切换App窗口的根控制器为主Navigation Controller,登录页会被系统自动回收,用户不存在返回登录页的路径。
- 打开Storyboard,选中你的主Navigation Controller,在Identity检查器里给它设置Storyboard ID,比如
MainNavigationController - 把登录成功的回调代码替换为如下内容:
@IBAction func signIn(_ sender: Any) { let signInConfig = GIDConfiguration(clientID: "xyz.apps.googleusercontent.com") GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in guard error == nil, let validUser = user else { return } // 实例化主导航控制器 let mainStoryboard = UIStoryboard(name: "Main", bundle: .main) guard let mainNav = mainStoryboard.instantiateViewController(withIdentifier: "MainNavigationController") as? UINavigationController, let window = UIApplication.shared.windows.first else { return } // 如果需要直接进入小票扫描页,直接设置导航栈的根页面,不需要push if let scanVC = mainStoryboard.instantiateViewController(withIdentifier: "scanReceiptsViewController") as? ViewController { mainNav.viewControllers = [scanVC] } // 切换根控制器,加淡入过渡动画避免生硬 window.rootViewController = mainNav UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: nil) } }
2. 实现启动自动登录逻辑
Google Sign-In会本地缓存用户登录凭证,你只需要在App启动阶段检查是否有有效历史登录态,有就直接进主界面,没有才展示登录页。
- 如果项目用了
SceneDelegate(iOS13+默认模板),把下面的代码写到scene(_:willConnectTo:options:)方法里 - 如果是老版本项目没有SceneDelegate,就把逻辑写到
AppDelegate的application(_:didFinishLaunchingWithOptions:)方法里
参考代码:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } let window = UIWindow(windowScene: windowScene) let mainStoryboard = UIStoryboard(name: "Main", bundle: .main) // 检查是否有历史登录记录 if GIDSignIn.sharedInstance.hasPreviousSignIn() { // 尝试恢复登录态 GIDSignIn.sharedInstance.restorePreviousSignIn { [weak self] user, error in if error == nil, let _ = user { // 登录态有效,直接加载主界面 guard let mainNav = mainStoryboard.instantiateViewController(withIdentifier: "MainNavigationController") as? UINavigationController else { return } window.rootViewController = mainNav } else { // 登录态失效,加载登录页(替换成你自己登录页的Storyboard ID) let loginVC = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController") window.rootViewController = loginVC } window.makeKeyAndVisible() self?.window = window } } else { // 无历史登录记录,加载登录页 let loginVC = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController") window.rootViewController = loginVC window.makeKeyAndVisible() self.window = window } }
注意事项
- 所有代码里用到的Storyboard ID必须和你在Storyboard里设置的完全一致,否则会触发运行时崩溃
- 后续如果要实现登出功能,先调用
GIDSignIn.sharedInstance.signOut()清除本地登录态,再把根控制器切回登录页即可 - 不要混用push、present两种方式做登录到主界面的跳转,切换根控制器是登录流最稳妥的实现方式,不会出现导航栈混乱的问题
内容的提问来源于stack exchange,提问作者Abdelrahman Hafrag
相关产品推荐
相关产品推荐

