Google Sign In needs a delegate that will receive all the feedback of the process.
import GoogleSignIn
class GoogleSignInDelegate: NSObject, GIDSignInDelegate, ObservableObject {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
guard error == nil else {
// Handle errors.
return
}
print("Google Sign In - success")
}
}
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
let googleSignDelegate = GoogleSignInDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GIDSignIn.sharedInstance().clientID = "xxxxxxxxxxxxx.apps.googleusercontent.com"
GIDSignIn.sharedInstance().delegate = googleSignDelegate
return true
}
// ...
}
Now, the Google Sign In button must be implemented. We can create our own custom view but we are going to use the default one provided by Google. Unfortunately, this button is not available in SwiftUI, to use it UIViewRepresentable
must be implemented.
import GoogleSignIn
import SwiftUI
struct GoogleSignInButton: UIViewRepresentable {
@Environment(\.colorScheme) var colorScheme
private var button: GIDSignInButton = GIDSignInButton()
func makeUIView(context: Context) -> GIDSignInButton {
button.colorScheme = self.colorScheme == .dark ? .dark : .light
return button
}
func updateUIView(_ uiView: UIViewType, context: Context) {
button.colorScheme = self.colorScheme == .dark ? .dark : .light
}
}
Using GIDSignInButton
is not necessary to call the signIn
method manually.
GIDSignIn.sharedInstance()?.signIn()
You’re almost ready to go, go to your ContentView
and replace its default body implementation for this one…
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Spacer()
GoogleSignInButton()
.frame(alignment: .center)
.padding(.horizontal, 50)
.navigationTitle("My Vocabulary")
}
}
}
}
Now run the project, press the button that appears at the top of the screen and you’ll see an error.
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'presentingViewController must be set.'
The Google Sign In shared instance has the property presentingViewController
that must refer to the view controller that will present the sign-in screen. To fix this, some changes must be done in our SceneDelegate
.
import UIKit
import SwiftUI
import GoogleSignIn
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let googleSignDelegate = (UIApplication.shared.delegate as! AppDelegate).googleSignDelegate
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
// Set the window root view controller as the `GIDSignIn` presenting view controller.
GIDSignIn.sharedInstance().presentingViewController = window.rootViewController
self.window = window
window.makeKeyAndVisible()
}
}
// ...
}
Run the project, press the button again, and select an account that you included in the list “test emails” during the Google Cloud Platform configuration.
Look for this message in the Xcode logs…
Google Sign In - success
Hurray!! 🎉 🥳 🎊
You have implemented the simplest sign-in with Google, no handling errors, no keeping session open after closing the app… But this will come later.