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

Swift集成Google登录时顶层视图控制器不显示且报错问题

Google登录报错Code=-5:用户取消登录流问题排查

错误现象

点击Google登录按钮无响应,再次点击时持续触发以下错误:
Error Domain=com.google.GIDSignIn Code=-5 "The user canceled the sign-in flow." UserInfo={NSLocalizedDescription=The user canceled the sign-in flow.}

已完成配置

  • 完成Google登录SDK配置
  • 将GIDClient添加到目标项目
  • 配置反向ClientID URL Scheme
  • 导入GoogleService.plist文件

现有代码

LoginView相关代码

@MainActor
final class AuthenticationViewModel: ObservableObject {
    func getRootViewController() -> UIViewController{
            guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else{
                return.init()
            }
            guard let root = screen.windows.first?.rootViewController else {
                return.init()
            }
            return root
        }
    func signInWithGoogle() async throws {
        let topVc = getRootViewController()
                
            let gidSignInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: topVc)
            
            guard let idToken: String = gidSignInResult.user.idToken?.tokenString else {
                throw URLError(.badServerResponse)
            }
            let accessToken: String = gidSignInResult.user.accessToken.tokenString
            
            let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
        
//        let authDataResult = try await Auth.auth().signIn(with: credential)
        print(credential)
    }
}

struct LoginView: View {
    @Binding var username: String
    @State private var password = ""
    @Binding var path: NavigationPath
    @State var errorMessage = ""
    @StateObject var viewModel = AuthenticationViewModel()    
    
    var body: some View {
        ZStack {
            Color("DarkGreen").edgesIgnoringSafeArea(.all)
            HStack(spacing: 70) {
                GoogleSignInButton(viewModel: GoogleSignInButtonViewModel(scheme: .dark, style: .icon, state:.normal)) {
                    Task {
                        do {
                            try await viewModel.signInWithGoogle()
                        } catch {
                            print(error)
                        }
                    }
                }
           }
    }
}

Utilities工具类代码

import UIKit

final class Utilities {
    
    static let shared = Utilities()
    private init() {}
    
    @MainActor
    func topViewController(controller: UIViewController? = nil) -> UIViewController? {
        let controller = controller ?? UIApplication.shared.keyWindow?.rootViewController
        
        if let navigationController = controller as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(controller: selected)
            }
        }
        if let presented = controller?.presentedViewController {
            return topViewController(controller: presented)
        }
        return controller
    }
}

问题分析与解决方案

核心问题在于顶层视图控制器获取逻辑错误:

  1. 原getRootViewController()仅获取根控制器,若当前页面处于导航栈或模态弹窗中,根控制器并非可见的顶层控制器,导致Google登录弹窗无法正确展示,触发"用户取消"的错误。
  2. Utilities中的topViewController方法使用了过时的UIApplication.shared.keyWindow,在iOS 13+多场景环境下无法正确获取活跃窗口。

修正步骤

  1. 更新AuthenticationViewModel的控制器获取逻辑,改用Utilities的顶层控制器方法:
@MainActor
final class AuthenticationViewModel: ObservableObject {
    func getTopViewController() -> UIViewController {
        guard let topVc = Utilities.shared.topViewController() else {
            return UIViewController()
        }
        return topVc
    }
    
    func signInWithGoogle() async throws {
        let topVc = getTopViewController()
                
        let gidSignInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: topVc)
        
        guard let idToken: String = gidSignInResult.user.idToken?.tokenString else {
            throw URLError(.badServerResponse)
        }
        let accessToken: String = gidSignInResult.user.accessToken.tokenString
        
        let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
    
        // let authDataResult = try await Auth.auth().signIn(with: credential)
        print(credential)
    }
}
  1. 修正Utilities中的顶层控制器获取方法,适配iOS 13+多场景:
import UIKit

final class Utilities {
    
    static let shared = Utilities()
    private init() {}
    
    @MainActor
    func topViewController(controller: UIViewController? = nil) -> UIViewController? {
        // 从活跃场景获取keyWindow,替代过时的全局keyWindow
        let baseController = controller ?? {
            guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
                  let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }),
                  let rootVc = keyWindow.rootViewController else {
                return nil
            }
            return rootVc
        }()
        
        guard let base = baseController else { return nil }
        
        if let navigationController = base as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = base as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(controller: selected)
            }
        }
        if let presented = base.presentedViewController {
            return topViewController(controller: presented)
        }
        return base
    }
}
  1. 额外检查项
  • 确认GoogleService.plist中的REVERSED_CLIENT_ID与项目Info.plist中的URL Scheme完全一致
  • 若使用最新版Google SignIn SDK,需在App启动时初始化客户端ID:
    // 在AppDelegate或SceneDelegate中添加
    GIDSignIn.sharedInstance.configuration = GIDConfiguration(clientID: "你的客户端ID")
    
  • 检查是否有其他弹窗(如权限请求)干扰Google登录弹窗的展示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 14:25:31