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 } }
问题分析与解决方案
核心问题在于顶层视图控制器获取逻辑错误:
- 原
getRootViewController()仅获取根控制器,若当前页面处于导航栈或模态弹窗中,根控制器并非可见的顶层控制器,导致Google登录弹窗无法正确展示,触发"用户取消"的错误。 - Utilities中的
topViewController方法使用了过时的UIApplication.shared.keyWindow,在iOS 13+多场景环境下无法正确获取活跃窗口。
修正步骤
- 更新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) } }
- 修正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 } }
- 额外检查项
- 确认
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
相关产品推荐
相关产品推荐

