How to pass rootViewController to Google sign-in in SwiftUi?

1.6k Views Asked by At

This is my first question here after years of finding every answer I needed.

I have coded some apps for iPhone in IOS, I'm not always sure of what I'm doing but they work!

They use Google sign in and it's working fine. Now I want to code an app for Mac OS and the problem I can't get through is this line:

GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) 

In this function:

func handleSignInButton() {
  GIDSignIn.sharedInstance.signIn(
    withPresenting: rootViewController) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        return
      }
      // If sign in succeeded, display the app's main content View.
    }
  )
}

In Google sign-in tutorial: https://developers.google.com/identity/sign-in/ios/sign-in

By what can I replace rootViewController in a Mac OS app?

I've tried something like this but I'm a bit lost :

GoogleSignInButton {
guard let presentingViewController = (NSApplication.shared.connectedScenes.first as? NSWindow)?.windows.first?.NSViewController else {return}
               
            let signInConfig = GIDConfiguration.init(clientID: XXXXXXXXX")
             GIDSignIn.sharedInstance.signIn(
               with: signInConfig,
               presentingWindow: presentingViewController) { user, error in
                 // check `error`; do something with `user`
             }
}

Ss they advise on https://github.com/google/GoogleSignIn-iOS

Google Sign-In allows your users to sign in to your native macOS app using their Google account and default browser. When building for macOS, the signInWithConfiguration: and addScopes: methods take a presentingWindow: parameter in place of presentingViewController:. Note that in order for your macOS app to store credentials via the Keychain on macOS, you will need to sign your app.

Has someone used Google sign-in with SwiftUI for a Mac OS app?

2

There are 2 best solutions below

0
On

If, when searching through a window, the Google authorization view opens and then immediately closes, then you have found the wrong view controller. in this case, it is better to use another, more crutch, but more stable method

You can place an empty UIViewController in View, and use it as a bridge for your button

struct RegistrationView: View {
    var body: some View {
       Button {
           GIDSignIn.sharedInstance.signIn(withPresenting: bridge.vc) { signInResult, error in }
       } label: {
            Text("Google")
       }
       .addBridge(bridge)
    }

    private let bridge = BridgeVCView()
}


struct BridgeVCView: UIViewControllerRepresentable {
    let vc = UIViewController()
    typealias UIViewControllerType = UIViewController
    
    func makeUIViewController(context: Context) -> UIViewController {
        return vc
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        //
    }
}

extension View {
    func addBridge(_ bridge: BridgeVCView) -> some View {
        self.background(bridge.frame(width: 0, height: 0))
    }
}
1
On

I haven't tested this, but at https://paulallies.medium.com/google-sign-in-swiftui-2909e01ea4ed, written in November 2021, I found:

guard let presentingViewController = (UIApplication.shared.connectedScenes.first
          as? UIWindowScene)?.windows.first?.rootViewController
      else {return}

I'm not 100% confident about taking the first of all of the scenes. You might need to account for not being the first, if your app supports multiple scenes.

I see another approach used in https://github.com/WesCSK/SwiftUI-Firebase-Authenticate-Using-Google-Sign-In. That sample uses old school UIApplicationDelegate methods, and if you're not using the newer SwiftUI application life cycle, might be easier to adapt. It's an iOS application, of course, but the app delegate logic should be similar to your Mac project.