Google Sign In and Firebase Authentication using SwiftUI

In this article we take a look at how to integrate Google Sign In and Firebase. We will use SwiftUI for this project. The full source code for this project can be found here.

\ The app won't have much UI this time around, since most of the heavy lifting will be done by Google's own log in UI. We will just add a simple "Log in with Google" button on the screen and when its tapped, should take the user to the Google Sign In UI.

\ However, let's first set up Firebase and Google Sign In:

1. Setting up Firebase Project:

Head over to Firebase.com and create a new account. Once logged in, create a new project in the Firebase Console.

  • Head over to Firebase.com and create a new account or log in.

  • Create a new project in Firebase Console

  • Once a new project is created, click Authentication.

  • Select "Get Started" and then make sure you enable "Google Sign In" under sign-in methods.

    \

Next, we will add our iOS app with the Firebase console. Here is how it goes:

  • Head over to "Project Overview" and select "Add iOS App".
  • This will ask you for your iOS app bundle ID. You can get this ID from the Xcode, select your Target > General > Bundle Identifier
  • Next, download the configuration file generated at the next step to your computer (GoogleService-Info.plist) - we will simple drag and drop this .plist file in the root folder of our Xcode Project

Next we will need to install Firebase SDK. If you don't have Firebase already install then you have one of two ways. You can use CocoaPods or Swift Package Manager. I used Swift Package Manager to install SDK on my Xcode.

Here is how to do this:

  • In Xcode go to File > Swift Packages > Add Package Dependency…
  • You will see a prompt appear, please add this link there: <https://github.com/firebase/firebase-ios-sdk.git>
  • Select the version of Firebase you would like to use. Make sure it's the latest one.
  • Choose the Firebase products you would like to install.

\

2. Setting up Google Sign In:

Next, we will install Google Sign in SDK. We can do this in one of two ways by either using CocoaPods or using Swift Package Manager. Let's use Swift Package Manager. Here is how:

  • In Xcode go to File > Swift Packages > Add Package Dependency…
  • You will see a prompt appear, please add this link there: <https://github.com/google/GoogleSignIn-iOS>
  • Click Add Package
  • \

And that's it. Now we have both Firebase and Google Sign In SDKs. Next, in Xcode head over to the `GoogleService-Info.plist` file that you just dragged into Xcode. and select the URL in front of `Reverse_Client_ID`. Copy this URL and then do this:

  • In the left panel of Xcode, click on the file with name of the your project. This is usually the first file.
  • Click the file under `Targets` and then click `Info`
  • Expand `URL Types` and click on the plus button to add a new URL type property.
  • Paste the `Reverse_Client_ID` in the `URL schemes` text field.

\

Implementation:

Now we are all set with the prerequisites. Now create an `AppDelegate` file and add the following code:

```javascript import UIKit import Firebase import GoogleSignIn

class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FirebaseApp.configure() return true }

func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -&gt; Bool {
  return GIDSignIn.sharedInstance.handle(url)
}

} ```

\ First of all, we are just importing the `Firebase` and `GoogleSignIn` modules. Then in `appdidfinishLaunching` method, we are configuring Firebase by adding the code `Firebase.confgure()`. Next, we are going to link Google Sign in by adding another method which returns a `GIDSignIn shared instance`.

In the `.app` file please add the following code in order to link the new `AppDelegate` file:

```javascript @main struct GoogleSignInFirebaseApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate //add this line

var body: some Scene {
    WindowGroup {
        ContentView()
    }
}

} ```

\ Now, we just need to implement the Google Sign in call back. Let's create a `LoginView.swift` file add the following code:

```javascript struct LoginView: View { @AppStorage("loginStatus") var loginStatus = false //1 @State var presentSheet: Bool = false // 2

var body: some View {

    
    ZStack {
      &#x2F;&#x2F;Just a simple button when tapped invokes signInWithGoogle method
        Button {
            signInWithGoogle()
        } label: {
            Text(&quot;Log In using Google&quot;)
                .padding()
                .background(Color.blue)
                .cornerRadius(20)
                .foregroundColor(Color.white)
        }
    }
    &#x2F;&#x2F;Presents HomeView modally
    .fullScreenCover(isPresented: $presentSheet) {
            HomeView()
    }


}

&#x2F;&#x2F; The main deal...

func signInWithGoogle() {
    guard let clientID = FirebaseApp.app()?.options.clientID else { return }

    &#x2F;&#x2F; Create Google Sign In configuration object.
    let config = GIDConfiguration(clientID: clientID)

    &#x2F;&#x2F; Start the sign in flow!
    GIDSignIn.sharedInstance.signIn(with: config, presenting: getRootViewController()) {  user, error in

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

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

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

        Auth.auth().signIn(with: credential) { result, err in
            if let err = error {
              print(err)
              return
            }

            guard let user = result?.user else { return }

            print(user.displayName ?? &quot;Success!&quot;)
            self.presentSheet = true
            withAnimation {
                self.loginStatus = true
            }

        }
    }
}

} ```

\ Let's go over in detail what is going on in `signInWithGoogle()` method. Initially, we are getting the `client ID` from Firebase, and then creating a Google Sign In `configuration` object using the `clientID`. Then once we have that we will start the sign in flow. We call the `GIDSignIn.sharedInstance.signIn` method which takes in the `configuration` object, and the root view controller. To extract the `rootViewController` please use the following extension on `View`:

\ ```javascript extension View { 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

}

} ```

\ The `GIDSignIn` method requires `rootViewController` so that it can present it's own view controller which presents the Google Sign In UI. Once the user signs in to the Google account and is successful in doing so, the callback returns with `error` and `result`. If there is no error present, we get user object with authentication id token. We use this authorization id token to create a credential. We will now use this credential to create a new user and sign into Firebase authentication using `Auth.auth().signin()` method. This method returns an error and result. If there is no error, we can get the user from the result object and toggle `presentSheet` boolean to true which will show the `HomeView`.

\ Also, here we are using `@AppStorage` property wrapper which acts like `UserDefaults` and saves the state of the user. So since we just signed in successfully to Firebase using Google Sign in, we will make `loginStatus` which is an `@AppStorage` variable to `true`. We will use this to do login persistence.

\ Now, in `HomeView.swift` file, lets add the following code:

\ ```javascript import SwiftUI import Firebase import GoogleSignIn

struct User { var id: String = "" var name: String = "" var email: String = ""

}

struct HomeView: View { @State var user = User() @AppStorage("loginStatus") var loginStatus = false var body: some View { VStack(spacing: 20) { Text("Welcome,\(user.name)") .onAppear { guard let user = Auth.auth().currentUser else { return } self.user = User(id: user.uid, name: user.displayName ?? "", email: user.email ?? "") }

        Button {
            GIDSignIn.sharedInstance.signOut()
            try? Auth.auth().signOut()

            withAnimation {
                self.loginStatus = false
            }
        } label: {
            Text(&quot;Log Out&quot;)
        }

    }
}

} ```

\ Here, we when the view appears, we are using the Firebase's `currentUser` method to get the logged in user info and then creating a `User` object and then showing the `user.displayName` in the `Text` view. Also we have implemented a Log Out button which when pressed simply logs out the user from Google and Firebase. And observe, that we again changing the `loginStatus` variable to `false`.

\ Now, in the `ContentView.swift` file add the following code:

```javascript struct ContentView: View { @AppStorage("loginStatus") var loginStatus = false var body: some View { if loginStatus { HomeView() }else { LoginView() }

}

} ```

\ We are checking the `loginStatus`, and if it is `false`, then we show the `LogInView` else we show the `HomeView`.

You can refer to our Firebase Authentication with Email and Password tutorial to know more about how to save User’s data (name, email, id) in a `Firestore` database and then retrieving it by using `user.uid` to query the database.

\