Firebase Authentication in SwiftUI
Sign in with Google
Learn how to authenticate user with Sign in with Google and Firebase Authentication
In the previous part of this series, we set up Firebase and implemented Firebase Anonymous Authentication, and prepared the AuthManager
class for signing in and linking accounts.
“You can let your users authenticate with Firebase using their Google Accounts by integrating Google Sign-In into your app.” ~ Firebase Documentation
Set up Sign in with Google
In order to do that, we need to add the Google Sign-In SDK to our SwiftUI project, and then enable Google Sign-In in your Firebase project.
I used
GoogleSignIn-iOS
version 7.0.0, which includes some changes from the previous versions. (e.g. how to configure the ClientID).
- Before implementing Google Sign-In, you need to add a custom URL scheme to Xcode project, to do so follow step number 1 here.
- Then, add the
GIDClientID
ininfo.plist
as explained here, copy theClientID
value from your GoogleServices-info.plist file.
Implement Google Sign-in
- Inside
AuthManager
, create a method namedgoogleAuth(user:)
that takesGIDGoogleUser
- Define
AuthCredentials
using the Google auth tokens (idToken
andaccessToken
) from the givenGIDGoogleUser
. - Then call the previously created
authenticateUser(credentials:)
and pass the credentials we just defined to link or sign-in with Firebase.
// import GoogleSignIn
func googleAuth(_ user: GIDGoogleUser) async throws -> AuthDataResult? {
guard let idToken = user.idToken?.tokenString else { return nil }
// 1.
let credentials = GoogleAuthProvider.credential(
withIDToken: idToken,
accessToken: user.accessToken.tokenString
)
do {
// 2.
return try await authenticateUser(credentials: credentials)
}
catch {
print("FirebaseAuthError: googleAuth(user:) failed. \(error)")
throw error
}
}
Create a new Swift file named GoogleSignInManager.swift in the Model folder, and add the following code:
import GoogleSignIn
class GoogleSignInManager {
static let shared = GoogleSignInManager()
typealias GoogleAuthResult = (GIDGoogleUser?, Error?) -> Void
private init() {}
@MainActor
func signInWithGoogle() async throws -> GIDGoogleUser? {
// 1.
if GIDSignIn.sharedInstance.hasPreviousSignIn() {
return try await GIDSignIn.sharedInstance.restorePreviousSignIn()
} else {
// 2.
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return nil }
guard let rootViewController = windowScene.windows.first?.rootViewController else { return nil }
// 3.
let result = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController)
return result.user
}
}
// 4.
func signOutFromGoogle() {
GIDSignIn.sharedInstance.signOut()
}
}
Here is what happens in this code snippet:
- Check if a user has previously signed in. If yes, then restore and return the user’s sign-in. Otherwise, move on to the regular sign-in process.
- Get the
rootViewController
by accessing it through the shared instance of your app'sUIApplication
instance. The Google Sign-In SDK usesrootViewController
to present the browser pop-up that hosts the sign-in flow. - Start the sign-in process by calling
signIn()
from the shared instance of theGIDSignIn
class. - Create a function that calls
signOut()
from the shared instance of theGIDSignIn
class to sign out from Google. This function will be called later inAuthManager
.
In LoginView
, import GoogleSignInSwift
, and uncomment GoogleSignInButton
.
To use Google Sign-In in your SwiftUI view, add the following code to your view, right after the body
:
func signInWithGoogle() async {
do {
guard let user = try await GoogleSignInManager.shared.signInWithGoogle() else { return }
let result = try await authManager.googleAuth(user)
if let result = result {
print("GoogleSignInSuccess: \(result.user.uid)")
dismiss()
}
}
catch {
print("GoogleSignInError: failed to sign in with Google, \(error))")
// Here you can show error message to user.
}
}
I used
GoogleSignInButton
for this demo, but you can create your custom button and callsignInWithGoogle()
from your button’s action.
Lastly, in AuthManager
, add a function named firebaseProviderSignOut(user:)
. In this function, iterate over all the providers for the signed in user, and check if one of the providers is google.com
. If so, call signOutFromGoogle
to sign out from Google.
Then, in the signOut
function, replace // TODO: Sign out from signed-in Provider.
with firebaseProviderSignOut(user)
.
func firebaseProviderSignOut(_ user: User) {
let providers = let providers = user.providerData
.map { $0.providerID }.joined(separator: ", ")
if providers.contains("google.com") {
GoogleSignInManager.shared.signOutFromGoogle()
}
}
Take it for a spin
Run the app on your phone or on the iOS Simulator. You should now be able to sign in and link with Google, as well as sign out again.
Note
If you authenticated anonymously and then linked with Google credentials, Firebase will not create a new user, but will update identifier and providers on the anonymous user.
Next Steps
Now that users can authenticate using Google, let’s add Sign in with Apple next.
Resources
“ Everyone has something to learn. Everyone has something to teach.” ~ Paul Hudson