Integrating GoogleSignIn in a UIKit app iOS 21.07.2021

Getting OAuth client ID

You will need an OAuth client ID and an iOS URL scheme to continue with the configuration in Xcode.

Head over to Google APIs Console and create a project for your app. If you have already created a project, you can also select it from the project list.

After you have created/selected a project, you also need to configure the OAuth consent screen. Follow the steps

  1. Select a project
  2. Select OAuth consent screen in left side menu
  3. Select External in User Type
  4. Follow other instructions
  5. Click Create

In the OAuth consent screen information page, fill in the application name, email and then click Save and continue. This application name will be the name being shown in the Google Sign-in form when a user tries to sign in using your app.

Once finished configuring the OAuth consent screen, it is time to create the OAuth client ID.

  1. Select Credentials in left side menu
  2. Click + Create Credentials
  3. Select OAuth client ID

Once you reach the OAuth client ID creation page, go ahead and select iOS as application type, fill in the name and also your sample app bundle ID and then click Create.

Click below OAuth 2.0 Client IDs and copy the OAuth client ID and iOS URL scheme you just create and keep them in somewhere easily reachable, you will need both of them in just a bit.

With that you have completed the OAuth client ID creation process, we can now head back to Xcode and proceed with the configuration.

Xcode

Before you can begin integrating your iOS app with the Google Sign-In components, you must download the dependencies and configure your Xcode project.

Install the Google Sign-In dependencies in your project

# CocoaPods

pod 'GoogleSignIn'

# Swift Package Manager

https://github.com/google/GoogleSignIn-iOS

Google Sign-in requires a custom URL Scheme to be added to your project. To add the custom scheme, follow the steps

  1. Select a project in Project navigator
  2. Select your Target
  3. Select Info tab in top bar
  4. Select URL Types and fill in the iOS URL scheme that you obtained while creating the OAuth client ID

Next, open AppDelegate.swift and import the GoogleSignIn module.

import GoogleSignIn

After that, update ``application(_:didFinishLaunchingWithOptions:)``.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    GIDSignIn.sharedInstance().clientID = "CID"
    GIDSignIn.sharedInstance().delegate = self
    GIDSignIn.sharedInstance()?.restorePreviousSignIn()

    return true
}

Next, add the following AppDelegate method implementation right after application(_:didFinishLaunchingWithOptions:).

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    var handled: Bool

    handled = GIDSignIn.sharedInstance().handle(url)
    if handled {
        return true
    }

    // Handle other custom URL types.
    // If not handled by this app, return false.
    return false
}

Lastly, let’s conform AppDelegate to GIDSignInDelegate and implement the sign(_:didSignInFor:withError:) method.

extension AppDelegate: GIDSignInDelegate {    
    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {

        if let error = error {
            if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
                print("The user has not signed in before or they have since signed out.")
            } else {
                print("\(error.localizedDescription)")
            }
            return
        }

        // Post notification after user successfully sign in
        NotificationCenter.default.post(name: .signInGoogleCompleted, object: nil)
    }
}

extension Notification.Name {    
    static var signInGoogleCompleted: Notification.Name {
        return .init(rawValue: #function)
    }
}

UIKit

To easy AutoLayout I'm going to use SnapKit and add all the UI elements into the view controller programmatically.

import UIKit
import SnapKit
import GoogleSignIn

class ViewController: UIViewController {
    var googleSignIn = GIDSignIn.sharedInstance()

    let statusLabel = UILabel()

    let signInButton: GIDSignInButton = {
        let v = GIDSignInButton()
        v.style = .wide
        v.addTarget(self, action: #selector(handleSignIn), for: .touchUpInside)
        return v
    }()

    let signOutButton: UIButton = {
        let v = UIButton(type: .system)
        v.setTitle("SignOut", for: .normal)
        v.addTarget(self, action: #selector(handleSignOut), for: .touchUpInside)
        return v
    }()

    lazy var stackView: UIStackView = {
        let v = UIStackView(arrangedSubviews: [statusLabel, signInButton, signOutButton])
        v.axis = .vertical
        v.distribution = .fillEqually
        v.spacing = 8
        v.alignment = .center
        return v
    }()

    // MARK: - Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()

        GIDSignIn.sharedInstance()?.presentingViewController = self
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(userDidSignInGoogle(_:)),
                                               name: .signInGoogleCompleted,
                                               object: nil)
    }

    // MARK: - Helpers

    private func setupView() {
        view.addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }

        updateScreen()
    }

    private func updateScreen() {
        if let user = GIDSignIn.sharedInstance()?.currentUser {
            statusLabel.text = "Hello \(user.profile.givenName!)!"
            signInButton.isHidden = true
            signOutButton.isHidden = false
            printUser(user: user)
        } else {
            statusLabel.text = "Please sign in"
            signInButton.isHidden = false
            signOutButton.isHidden = true
        }
    }

    private func printUser(user: GIDGoogleUser?) {
        guard let user = user else {
             print("Uh oh. The user cancelled the Google login.")
             return
         }
         let userId = user.userID ?? ""
         print("Google User ID: \(userId)")

         let userIdToken = user.authentication.idToken ?? ""
         print("Google ID Token: \(userIdToken)")

         let userFirstName = user.profile.givenName ?? ""
         print("Google User First Name: \(userFirstName)")

         let userLastName = user.profile.familyName ?? ""
         print("Google User Last Name: \(userLastName)")

         let userEmail = user.profile.email ?? ""
         print("Google User Email: \(userEmail)")

         let googleProfilePicURL = user.profile.imageURL(withDimension: 150)?.absoluteString ?? ""
         print("Google Profile Avatar URL: \(googleProfilePicURL)")
    }

    // MARK: - Handlers

    @objc func handleSignIn() {
        GIDSignIn.sharedInstance()?.signIn()
    }

    @objc func handleSignOut() {
        GIDSignIn.sharedInstance()?.signOut()
        updateScreen()
    }

    @objc private func userDidSignInGoogle(_ notification: Notification) {
        updateScreen()
    }
}