How to pass the presenting view controller and client ID for your app to the Google Sign In sign-in method

Viewed 1119

Hello I have been following (https://firebase.google.com/docs/auth/ios/google-signin) to setup a simple Google SignIn with Firebase in an iOS app. I use Xcode 12.5.1. I created a new Storyboard (UIKit) app. I did pod init and added Firebase and GoogleSignIn and did pod install.

I'm also total noob in iOS.

I'm trying to follow the steps and for non of the steps it really explains where to put the code. For some I think I succeeded, but for the step 4. I have no idea where to put that code. I tried looking for other posts/tutorials for setting up GoogleSignIn with Firebase and SwiftUI and non of them work. Steps seem pretty simple, but they probably omit something very simple that a noob in iOS doesn't know.

Can some1 please explain a bit this step 4 at least.

Some of you commented to put it in the AppDelegate. I have created AppDelegate, but not sure where to put it there. Creating a signIn function in AppDelegate for dumping the code from the step 4 does not work. I also tried to wrap it with UIViewControllerRepresentable like here with the same problem.

It complains that this presenting: self from GIDSignIn.sharedInstance.signIn(with: config, presenting: self) { [unowned self] user, error in (code from step 4 that I don't know where to put) is AppDelegate that can't be converted to UIViewController that is expected.

import SwiftUI
import Firebase
import GoogleSignIn

@main
struct FolderlesApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}


class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        FirebaseApp.configure()
        
        return true
    }
    
    @available(iOS 9.0, *)
    func application(_ application: UIApplication, open url: URL,
                     options: [UIApplication.OpenURLOptionsKey: Any])
      -> Bool {
      return GIDSignIn.sharedInstance.handle(url)
    }
}
1 Answers

I think you are using SwfitUI not UIkit in the code you provided. If this is true. I can answer the SwiftUI solution.

Assume you have already set up the Step 1 (URL Types) and import the GoogleService-Info.plist. If you don’t, make sure to do it first.

Then, we have to create AppDelegate by ourselves in SwiftUI. You can add this code into your “App” type struct file (usually, the file name is your Project_NameApp.swift). Just like the example down below. This is step 2, step 3 in Authenticate Using Google Sign-In on iOS  |  Firebase

// Project_NameApp.swift
struct Project_NameApp: App {
    // add this var define line
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    let persistenceController = PersistenceController.shared
    
    var body: some Scene {
        WindowGroup {
                TestLoginView()
        }
    }
}

// add this class
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        // add this function for init FirebassApp 
        FirebaseApp.configure()
        return true
    }
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
           // add this function for Google Sign in to handle the url
        return GIDSignIn.sharedInstance.handle(url)
    }
}

The next one is your main question - Where the step 4’s code should we be put? Following code is the example SwiftUI view that you want to login with google. You can see it just turn the step 4 code into the function, and bind to the Button you want the user to tap and Sign in with a Google account.

import SwiftUI
import GoogleSignIn
import Firebase

struct TestLoginView: View {
    var body: some View {
        VStack {
            Button(action: {
                   // Just add the action function, it will works.
                googleSignIn()
            }, label: {
                Text("Google Sign In")
            })
        }
    }
    
    func googleSignIn(){
        guard let clientID = FirebaseApp.app()?.options.clientID else { return }
        
        // Create Google Sign In configuration object.
        let config = GIDConfiguration(clientID: clientID)
        
        // Start the sign in flow!
        GIDSignIn.sharedInstance.signIn(with: config, presenting: (UIApplication.shared.windows.first?.rootViewController)!) { user, error in

          if let error = error {
            print(error.localizedDescription)
            return
          }

          guard
            let authentication = user?.authentication,
            let idToken = authentication.idToken
          else {
            return
          }

            let credential = GoogleAuthProvider.credential(withIDToken: idToken,
                            accessToken: authentication.accessToken)

            // Authenticate with Firebase using the credential object
            Auth.auth().signIn(with: credential) { (authResult, error) in
                if let error = error {
                    print("authentication error \(error.localizedDescription)")
                    return
                }
                print(authResult ?? "none")
            }
        }
    }
}

struct TestLoginView_Previews: PreviewProvider {
    static var previews: some View {
        TestLoginView()
    }
}

I have tested it on my Xcode and it works for me. You can try it and hope you can solve the problem!

Tips 1: This is just a simple code. If you want a better implementation. I recommend you implement with MVVM structure. In this case, you should put the function into your ViewModel which you create by yourself. The button should call the Google Sign in function in your ViewModel.

Tips 2: This code doesn’t implement the multi-factor login with phone verify, check out this article for more detail. Most use case won’t use this less frequently used multi-factor login.

Related