Adicionar o recurso "Fazer login com o Google" a um app iOS

1. Antes de começar

Este codelab mostra como criar um aplicativo iOS que implementa o recurso Fazer login com o Google e é executado em um simulador. Implementações usando SwiftUI e UIKit são fornecidas.

O SwiftUI é o framework de interface moderno da Apple para o desenvolvimento de novos apps. Ele permite criar interfaces de usuário para todas as plataformas da Apple com uma única base de código compartilhada. É necessário ter no mínimo a versão 13 do iOS.

O UIKit é o framework de UI original e fundamental da Apple para iOS. Ela oferece compatibilidade com versões anteriores do iOS. Isso a torna uma boa opção para apps estabelecidos que precisam oferecer suporte a vários dispositivos mais antigos.

Siga o caminho do framework que melhor se alinha às suas necessidades de desenvolvimento.

Pré-requisitos

O que você vai aprender

  • Como criar um projeto do Google Cloud
  • Como criar clientes OAuth no Console do Google Cloud
  • Como implementar o recurso Fazer login com o Google no seu app iOS
  • Como personalizar o botão "Fazer login com o Google"
  • Como decodificar um token de ID
  • Como ativar o App Check no seu app iOS

O que é necessário

Este codelab foi criado usando o Xcode 16.3 com o simulador do iOS 18.3. Use a versão mais recente do Xcode para desenvolvimento.

2. Criar um projeto do Xcode

  1. Abra o Xcode e selecione Criar um projeto do Xcode.
  2. Escolha a guia iOS, selecione o modelo App e clique em Next.

Página do modelo de criação de projeto do Xcode

  1. Nas opções do projeto:
    • Insira o Nome do produto.
    • Se quiser, selecione sua Equipe.
    • Insira o identificador da organização.
    • Anote o identificador do pacote gerado. Você precisará dessas informações posteriormente.
    • Em Interface, escolha uma das opções:
      • SwiftUI para um app baseado em SwiftUI.
      • Storyboard para um app baseado em UIKit.
    • Escolha Swift para o Idioma.
    • Clique em Próxima e escolha um local para salvar o projeto.

Página de opções do projeto do Xcode

3. Criar um cliente OAuth

Para permitir que seu app se comunique com os serviços de autenticação do Google, crie um ID do cliente OAuth. Isso requer um projeto do Google Cloud. As etapas a seguir orientam você no processo de criação de um projeto e um ID do cliente OAuth.

Selecionar ou criar um projeto do Google Cloud

  1. Acesse o Console do Google Cloud e selecione ou crie um projeto. Se você selecionar um projeto preexistente, o console vai direcionar você automaticamente para a próxima etapa necessária.

Página do seletor de projetos do console do Google Cloud

  1. Insira um nome para seu novo projeto do Google Cloud.
  2. Selecione Criar.

Página do seletor de projetos do console do Google Cloud

Se você já tiver configurado uma tela de consentimento para o projeto selecionado, não será necessário fazer isso agora. Nesse caso, pule esta seção e vá para Criar um cliente OAuth 2.0.

  1. Selecione Configurar tela de consentimento.

Página de criação do cliente OAuth no console do Google Cloud com requisito de configuração da tela de permissão

  1. Selecione Começar na página de branding.

Página de início da marca do console do Google Cloud

  1. Na página de configuração do projeto, preencha os seguintes campos:
    • Informações do app: insira um nome e um e-mail de suporte ao usuário para seu app. Esse e-mail de suporte será exibido publicamente para que os usuários entrem em contato com você se tiverem dúvidas sobre o consentimento deles.
    • Público-alvo: selecione Externo.
    • Dados de contato: digite um endereço de e-mail para que o Google entre em contato com você sobre seu projeto.
    • Leia a Política de dados do usuário dos serviços de API do Google.
    • Clique em Criar.

Página de configuração de branding do cliente do Console do Google Cloud

  1. Selecione a página Clientes no menu de navegação.
  2. Clique em Criar cliente.

Página de clientes do projeto do Google Cloud

Criar um cliente OAuth 2.0

  1. Selecione iOS em Tipo de aplicativo.
  2. Insira um nome para o cliente.
  3. Insira o Identificador do pacote criado na última etapa.
  4. Insira o ID da equipe atribuído a ela pela Apple. Por enquanto, essa etapa é opcional, mas um ID da equipe é necessário para ativar o App Check mais tarde neste codelab.
  5. Selecione Criar.

Página de entrada de detalhes do cliente OAuth

  1. Copie o ID do cliente da janela de diálogo. Você vai precisar dele mais tarde.
  2. Faça o download do arquivo plist para consultar depois.

Caixa de diálogo "ID do cliente OAuth criado"

4. Configurar o projeto do Xcode

A próxima etapa é configurar seu projeto Xcode para trabalhar com o SDK Sign-in com o Google. Esse processo envolve adicionar o SDK ao seu projeto como uma dependência e configurar as definições do projeto com um ID do cliente exclusivo. Esse ID permite que o SDK se comunique com segurança com o serviço de autenticação do Google durante o processo de login.

Instalar as dependências do recurso "Fazer login com o Google"

  1. Abra seu projeto do Xcode.
  2. Navegue até Arquivo > Adicionar dependências de pacote.
  3. Na barra de pesquisa, insira o URL do repositório "Fazer login com o Google": https://github.com/google/GoogleSignIn-iOS

Encontrar a dependência "Fazer login com o Google" no Swift Package Manager

  1. Selecione Adicionar pacote.
  2. Selecione o destino principal do aplicativo para o pacote GoogleSignIn.
  3. Se você estiver usando a SwiftUI, selecione o destino principal do aplicativo para o pacote GoogleSignInSwift. Se você planeja usar o UIKit, não selecione um destino para este pacote.
  4. Selecione Adicionar pacote.

Adicionar a dependência "Fazer login com o Google" ao seu projeto

Configurar as credenciais do app

  1. No Project Navigator, clique na raiz do projeto.
  2. Na área principal do editor, selecione o destino principal do aplicativo na lista TARGETS.
  3. Selecione a guia Informações na parte de cima da área do editor.
  4. Passe o cursor sobre a última linha na seção Propriedades de destino personalizadas do iOS e clique no botão + que aparece.

Adicionar uma nova chave de segmentação às propriedades de segmentação do iOS

  1. Na coluna Chave, digite GIDClientID.
  2. Na coluna Valor, cole o ID do cliente que você copiou do console do Google Cloud.

Adicione GIDClientID ao destino principal do app

  1. Abra o arquivo plist que você baixou do console do Google Cloud.
  2. Copie o valor de ID do cliente invertido.

Arquivo plist do console do Google Cloud

  1. Expanda Tipos de URL na parte de baixo da guia Informações.
  2. Selecione o botão +
  3. Insira o ID do cliente invertido na caixa Esquemas de URL.

Adicionar a chave URLSchemes ao destino principal do aplicativo

Agora já podemos começar a adicionar o botão de login ao app.

5. Adicionar o botão de login

Com o projeto do Xcode configurado, é hora de começar a adicionar o botão "Fazer login com o Google" ao app.

A lógica principal dessa etapa é a chamada para GIDSignIn.sharedInstance.signIn. Esse método inicia o processo de autenticação, transferindo o controle para o SDK Fazer login com o Google para apresentar o fluxo de login com o Google ao usuário.

SwiftUI

  1. Encontre o arquivo ContentView.swift no navegador de projetos do Xcode.
  2. Substitua o conteúdo do arquivo pelo seguinte texto:
import GoogleSignIn
import GoogleSignInSwift
import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
      GoogleSignInButton(action: handleSignInButton).padding()
    }
  }

  func handleSignInButton() {
    // Find the current window scene.
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
      print("There is no active window scene")
      return
    }

    // Get the root view controller from the window scene.
    guard
      let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
        .rootViewController
    else {
      print("There is no key window or root view controller")
      return
    }

    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(
      withPresenting: rootViewController
    ) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }
      // If sign in succeeded, display the app's main content View.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }
}

#Preview {
  ContentView()
}

Botão "Fazer login com o Google" do framework SwiftUI no simulador do iOS

UIKit

  1. Encontre o arquivo ViewController.swift no navegador de projetos do Xcode.
  2. Substitua o conteúdo do arquivo pelo seguinte texto:
import GoogleSignIn
import UIKit

class ViewController: UIViewController {

  // Create an instance of the Sign in with Google button
  let signInButton = GIDSignInButton()

  override func viewDidLoad() {
    super.viewDidLoad()

    // Add the sign-in button to your view
    view.addSubview(signInButton)

    // Position the button using constraints
    signInButton.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signInButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    ])

    // Add a target to the button to call a method when it's pressed
    signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)
  }

  // This method is called when the sign-in button is pressed.
  @objc func signInButtonTapped() {
    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      // If sign in succeeded, print the ID token.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }
}

Botão "Fazer login com o Google" do framework UIKit no simulador do iOS

Ver o botão de login

Inicie o app no simulador. O botão "Fazer login com o Google" vai aparecer, mas ainda não vai funcionar corretamente. Isso é esperado, já que você ainda precisa implementar o código para processar o redirecionamento de volta ao app depois que o usuário se autentica.

6. Personalizar o botão de login

É possível personalizar o botão padrão "Fazer login com o Google" para combinar melhor com o tema do seu app. Com o SDK Sign-in com o Google, é possível modificar o esquema de cores e o estilo do botão.

SwiftUI

O botão padrão é adicionado à página com esta linha de código:

GoogleSignInButton(action: handleSignInButton)

O GoogleSignInButton é personalizado transmitindo parâmetros ao inicializador dele. O código a seguir faz com que o botão de login apareça no modo escuro.

  1. Abra ContentView.swift.
  2. Atualize o inicializador do GoogleSignInButton para conter os seguintes valores:
GoogleSignInButton(
  scheme: .dark,  // Options: .light, .dark, .auto
  style: .standard,  // Options: .standard, .wide, .icon
  state: .normal,  // Options: .normal, .disabled
  action: handleSignInButton
).padding()

Botão "Fazer login com o Google" no modo escuro do framework SwiftUI no simulador do iOS

Para mais informações sobre as opções de personalização, consulte a referência do framework GoogleSignInSwift.

UIKit

O botão padrão é criado com estas linhas de código:

// Create an instance of the Sign in with Google button
let signInButton = GIDSignInButton()

// Add the button to your view
view.addSubview(signInButton)

O GIDSignInButton é personalizado definindo propriedades na instância do botão. O código a seguir faz com que o botão de login apareça no modo escuro.

  1. Abra ViewController.swift.
  2. Adicione as seguintes linhas de código imediatamente antes de adicionar o botão de login à visualização na função viewDidLoad:
// Set the width and color of the sign-in button
signInButton.style = .standard  // Options: .standard, .wide, .iconOnly
signInButton.colorScheme = .dark  // Options: .dark, .light

Botão "Fazer login com o Google" no modo escuro do framework UIKit no simulador do iOS

Para mais informações sobre personalização, consulte a Referência do framework GoogleSignIn.

7. Processar o URL de redirecionamento de autenticação

Com o botão de login adicionado, a próxima etapa é processar o redirecionamento que ocorre depois que um usuário se autentica. Após a autenticação, o Google retorna um URL com um código de autorização temporário. Para concluir o processo de login, um manipulador intercepta essa URL e a transmite ao SDK Sign-in com o Google para ser trocada por um token de ID assinado (JWT).

SwiftUI

  1. Abra o arquivo que contém sua estrutura App. O nome desse arquivo é baseado no seu projeto, então será algo como YourProjectNameApp.swift.
  2. Substitua o conteúdo do arquivo pelo seguinte texto:
import GoogleSignIn
import SwiftUI

@main
struct iOS_Sign_in_with_Google_App: App {
  var body: some Scene {
    WindowGroup {
      ContentView()

        .onOpenURL { url in
          GIDSignIn.sharedInstance.handle(url)
        }
    }
  }
}

UIKit

  1. Abra AppDelegate.swift.
  2. Adicione a seguinte importação à parte de cima do arquivo:
import GoogleSignIn
  1. Adicione a seguinte função de gerenciador de autenticação na classe AppDelegate. Um bom lugar para colocar isso é logo após a chave de fechamento do método 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
  }
  // If not handled by this app, return false.
  return false
}

Depois de fazer essas mudanças, o arquivo AppDelegate.swift vai ficar assim:

import GoogleSignIn
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // Override point for customization after application launch.
    return true
  }

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

    handled = GIDSignIn.sharedInstance.handle(url)
    if handled {
      return true
    }
    // If not handled by this app, return false.
    return false
  }

  // MARK: UISceneSession Lifecycle

  func application(
    _ application: UIApplication,
    configurationForConnecting connectingSceneSession: UISceneSession,
    options: UIScene.ConnectionOptions
  ) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return UISceneConfiguration(
      name: "Default Configuration",
      sessionRole: connectingSceneSession.role
    )
  }

  func application(
    _ application: UIApplication,
    didDiscardSceneSessions sceneSessions: Set<UISceneSession>
  ) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
  }
}

Testar o fluxo de login

Agora você pode testar o fluxo de login completo.

Execute o app e toque no botão de login. Depois da autenticação, o Google vai mostrar uma tela de consentimento em que você pode conceder permissão para o app acessar suas informações. Depois que você aprovar, o login será concluído, e você vai voltar ao app.

Quando o fluxo de login é concluído, o SDK Sign-in with Google armazena com segurança as credenciais do usuário no Keychain do dispositivo. Essas credenciais podem ser usadas mais tarde para permitir que um usuário permaneça conectado em inicializações subsequentes do app.

8. Adicionar um botão de logout

Agora que o login está funcionando, a próxima etapa é adicionar um botão de logout e atualizar a interface para refletir o estado de login atual do usuário. Quando um login é bem-sucedido, o SDK fornece um objeto GIDGoogleUser. Esse objeto contém uma propriedade profile com informações básicas, como o nome e o e-mail do usuário, que você vai usar para personalizar a interface.

SwiftUI

  1. Abra o arquivo ContentView.swift.
  2. Adicione uma variável de estado na parte de cima da sua struct ContentView. Essa variável vai armazenar as informações do usuário depois que ele fizer login. Como é uma variável @State, o SwiftUI atualiza automaticamente a interface sempre que o valor dela muda:
struct ContentView: View {
  @State private var user: GIDGoogleUser?
}
  1. Substitua o body atual da sua estrutura ContentView pelo seguinte VStack. Isso vai verificar se a variável de estado user contém um usuário. Se isso acontecer, uma mensagem de boas-vindas e um botão de sair vão aparecer. Caso contrário, o botão original "Fazer login com o Google" vai aparecer:
var body: some View {
  VStack {
    // Check if the user is signed in.
    if let user = user {
      // If signed in, show a welcome message and the sign-out button.
      Text("Hello, \(user.profile?.givenName ?? "User")!")
        .font(.title)
        .padding()

      Button("Sign Out", action: signOut)
        .buttonStyle(.borderedProminent)

    } else {
      // If not signed in, show the "Sign in with Google" button.
      GoogleSignInButton(
        scheme: .dark,  // Options: .light, .dark, .auto
        style: .standard,  // Options: .standard, .wide, .icon
        state: .normal,  // Options: .normal, .disabled
        action: handleSignInButton
      ).padding()
    }
  }
}
  1. Atualize o bloco de conclusão handleSignInButton para atribuir signInResult.user à nova variável user. Isso é o que faz a interface mudar para a visualização conectada:
func handleSignInButton() {
  // Find the current window scene.
  guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
    print("There is no active window scene")
    return
  }

  // Get the root view controller from the window scene.
  guard
    let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
      .rootViewController
  else {
    print("There is no key window or root view controller")
    return
  }

  // Start the sign-in process.
  GIDSignIn.sharedInstance.signIn(
    withPresenting: rootViewController
  ) { signInResult, error in
    guard let result = signInResult else {
      // Inspect error
      print("Error signing in: \(error?.localizedDescription ?? "No error description")")
      return
    }

    DispatchQueue.main.async {
      self.user = result.user
    }

    // If sign in succeeded, display the app's main content View.
    print("ID Token: \(result.user.idToken?.tokenString ?? "")")
  }
}
  1. Adicione uma nova função signOut à parte de baixo da struct ContentView para ser chamada pelo botão de sair:
func signOut() {
  GIDSignIn.sharedInstance.signOut()
  // After signing out, set the `user` state variable to `nil`.
  self.user = nil
}

Inicie o app e faça login. A interface vai mudar após uma autenticação bem-sucedida.

Estado conectado do framework SwiftUI no simulador do iOS

Depois de fazer essas mudanças, o arquivo ContentView.swift vai ficar assim:

import GoogleSignIn
import GoogleSignInSwift
import SwiftUI

struct ContentView: View {

  @State private var user: GIDGoogleUser?

  var body: some View {
    VStack {
      // Check if the user is signed in.
      if let user = user {
        // If signed in, show a welcome message and the sign-out button.
        Text("Hello, \(user.profile?.givenName ?? "User")!")
          .font(.title)
          .padding()

        Button("Sign Out", action: signOut)
          .buttonStyle(.borderedProminent)

      } else {
        // If not signed in, show the "Sign in with Google" button.
        GoogleSignInButton(
          scheme: .dark,  // Options: .light, .dark, .auto
          style: .standard,  // Options: .standard, .wide, .icon
          state: .normal,  // Options: .normal, .disabled
          action: handleSignInButton
        ).padding()
      }
    }
  }

  func handleSignInButton() {
    // Find the current window scene.
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
      print("There is no active window scene")
      return
    }

    // Get the root view controller from the window scene.
    guard
      let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
        .rootViewController
    else {
      print("There is no key window or root view controller")
      return
    }

    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(
      withPresenting: rootViewController
    ) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      DispatchQueue.main.async {
        self.user = result.user
      }

      // If sign in succeeded, display the app's main content View.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }

  func signOut() {
    GIDSignIn.sharedInstance.signOut()
    // After signing out, set the `user` state variable to `nil`.
    self.user = nil
  }
}

#Preview {
  ContentView()
}

UIKit

  1. Abra ViewController.swift.
  2. Na parte de cima do ViewController, logo abaixo de onde você declarou o signInButton, adicione um botão de sair e um rótulo de boas-vindas:
let signOutButton = UIButton(type: .system)
let welcomeLabel = UILabel()
  1. Adicione a seguinte função à parte de baixo do ViewController. Essa função vai mostrar uma interface diferente para o usuário com base no status de login dele:
private func updateUI(for user: GIDGoogleUser?) {
  if let user = user {
    // User is signed in.
    signInButton.isHidden = true
    signOutButton.isHidden = false
    welcomeLabel.isHidden = false
    welcomeLabel.text = "Hello, \(user.profile?.givenName ?? "User")!"
  } else {
    // User is signed out.
    signInButton.isHidden = false
    signOutButton.isHidden = true
    welcomeLabel.isHidden = true
  }
}
  1. Na parte de baixo da função viewDidLoad, adicione o seguinte código para incluir o rótulo de boas-vindas e o botão de sair na visualização:
// --- Set up the Welcome Label ---
welcomeLabel.translatesAutoresizingMaskIntoConstraints = false
welcomeLabel.textAlignment = .center
welcomeLabel.font = .systemFont(ofSize: 24, weight: .bold)
view.addSubview(welcomeLabel)

NSLayoutConstraint.activate([
  welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
  welcomeLabel.bottomAnchor.constraint(equalTo: signInButton.topAnchor, constant: -20),
])

// --- Set up the Sign-Out Button ---
signOutButton.translatesAutoresizingMaskIntoConstraints = false
signOutButton.setTitle("Sign Out", for: .normal)
view.addSubview(signOutButton)

NSLayoutConstraint.activate([
  signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
  signOutButton.topAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 20),
])

signOutButton.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)

// --- Set Initial UI State ---
updateUI(for: nil)
  1. Atualize a função signInButtonTapped para chamar o método UpdateUI em um login bem-sucedido:
@objc func signInButtonTapped() {
  // Start the sign-in process.
  GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
    guard let result = signInResult else {
      // Inspect error
      print("Error signing in: \(error?.localizedDescription ?? "No error description")")
      return
    }

    // If sign in succeeded, print the ID token.
    print("ID Token: \(result.user.idToken?.tokenString ?? "")")

    DispatchQueue.main.async {
      self.updateUI(for: result.user)
    }
  }
}
  1. Por fim, adicione uma função signOutButtonTapped ao ViewController para processar o processo de encerramento da sessão:
@objc func signOutButtonTapped() {
  GIDSignIn.sharedInstance.signOut()
  // Update the UI for the signed-out state.
  updateUI(for: nil)
}

Inicie o app e faça login. A interface vai mudar após uma autenticação bem-sucedida.

Estado conectado do framework UIKit no simulador do iOS

Depois de fazer essas mudanças, o arquivo ViewController.swift vai ficar assim:

import GoogleSignIn
import UIKit

class ViewController: UIViewController {

  // Create an instance of the Sign in with Google button
  let signInButton = GIDSignInButton()
  let signOutButton = UIButton(type: .system)
  let welcomeLabel = UILabel()

  override func viewDidLoad() {
    super.viewDidLoad()

    // Set the width and color of the sign-in button
    signInButton.style = .standard  // Options: .standard, .wide, .iconOnly
    signInButton.colorScheme = .dark  // Options: .dark, .light

    // Add the sign-in button to your view
    view.addSubview(signInButton)

    // Position the button using constraints
    signInButton.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signInButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    ])

    // Add a target to the button to call a method when it's pressed
    signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)

    // --- Set up the Welcome Label ---
    welcomeLabel.translatesAutoresizingMaskIntoConstraints = false
    welcomeLabel.textAlignment = .center
    welcomeLabel.font = .systemFont(ofSize: 24, weight: .bold)
    view.addSubview(welcomeLabel)

    NSLayoutConstraint.activate([
      welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      welcomeLabel.bottomAnchor.constraint(equalTo: signInButton.topAnchor, constant: -20),
    ])

    // --- Set up the Sign-Out Button ---
    signOutButton.translatesAutoresizingMaskIntoConstraints = false
    signOutButton.setTitle("Sign Out", for: .normal)
    view.addSubview(signOutButton)

    NSLayoutConstraint.activate([
      signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signOutButton.topAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 20),
    ])

    signOutButton.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)

    // --- Set Initial UI State ---
    updateUI(for: nil)
  }

  // This method is called when the sign-in button is pressed.
  @objc func signInButtonTapped() {
    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      // If sign in succeeded, print the ID token.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")

      DispatchQueue.main.async {
        self.updateUI(for: result.user)
      }
    }
  }

  private func updateUI(for user: GIDGoogleUser?) {
    if let user = user {
      // User is signed in.
      signInButton.isHidden = true
      signOutButton.isHidden = false
      welcomeLabel.isHidden = false
      welcomeLabel.text = "Hello, \(user.profile?.givenName ?? "User")!"
    } else {
      // User is signed out.
      signInButton.isHidden = false
      signOutButton.isHidden = true
      welcomeLabel.isHidden = true
    }
  }

  @objc func signOutButtonTapped() {
    GIDSignIn.sharedInstance.signOut()
    // Update the UI for the signed-out state.
    updateUI(for: nil)
  }
}

9. Restaurar o estado de login de um usuário

Para melhorar a experiência dos usuários recorrentes, a próxima etapa é restaurar o estado de login deles ao iniciar o app. A chamada de restorePreviousSignIn usa as credenciais salvas no Keychain para fazer login silenciosamente, garantindo que o usuário não precise concluir o fluxo de login todas as vezes.

SwiftUI

  1. Abra ContentView.swift.
  2. Adicione o código abaixo diretamente após o VStack na variável body:
.onAppear {
  // On appear, try to restore a previous sign-in.
  GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
    // This closure is called when the restoration is complete.
    if let user = user {
      // If a user was restored, update the `user` state variable.
      DispatchQueue.main.async {
        self.user = user
      }

      // Print the ID token to the console when restored.
      print("Restored ID Token: \(user.idToken?.tokenString ?? "")")
    }
  }
}

O arquivo ContentView.swift vai ficar assim:

import GoogleSignIn
import GoogleSignInSwift
import SwiftUI

struct ContentView: View {

  @State private var user: GIDGoogleUser?

  var body: some View {
    VStack {
      // Check if the user is signed in.
      if let user = user {
        // If signed in, show a welcome message and the sign-out button.
        Text("Hello, \(user.profile?.givenName ?? "User")!")
          .font(.title)
          .padding()

        Button("Sign Out", action: signOut)
          .buttonStyle(.borderedProminent)

      } else {
        // If not signed in, show the "Sign in with Google" button.
        GoogleSignInButton(
          scheme: .dark,  // Options: .light, .dark, .auto
          style: .standard,  // Options: .standard, .wide, .icon
          state: .normal,  // Options: .normal, .disabled
          action: handleSignInButton
        ).padding()
      }
    }

    .onAppear {
      // On appear, try to restore a previous sign-in.
      GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
        // This closure is called when the restoration is complete.
        if let user = user {
          // If a user was restored, update the `user` state variable.
          DispatchQueue.main.async {
            self.user = user
          }

          // Print the ID token to the console when restored.
          print("Restored ID Token: \(user.idToken?.tokenString ?? "")")
        }
      }
    }
  }

  func handleSignInButton() {
    // Find the current window scene.
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
      print("There is no active window scene")
      return
    }

    // Get the root view controller from the window scene.
    guard
      let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
        .rootViewController
    else {
      print("There is no key window or root view controller")
      return
    }

    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(
      withPresenting: rootViewController
    ) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      DispatchQueue.main.async {
        self.user = result.user
      }

      // If sign in succeeded, display the app's main content View.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }

  func signOut() {
    GIDSignIn.sharedInstance.signOut()
    // After signing out, set the `user` state variable to `nil`.
    self.user = nil
  }
}

#Preview {
  ContentView()
}

UIKit

  1. Abra ViewController.swift.
  2. Adicione a seguinte chamada restorePreviousSignIn ao final do método viewDidLoad:
// Attempt to restore a previous sign-in session
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
  if let user = user {
    print("Successfully restored sign-in for user: \(user.profile?.givenName ?? "Unknown")")

    // Print the ID token when a session is restored.
    print("Restored ID Token: \(user.idToken?.tokenString ?? "")")

    // On success, update the UI for the signed-in state on the main thread.
    DispatchQueue.main.async {
      self.updateUI(for: user)
    }
  }
}

O arquivo ViewController.swift vai ficar assim:

import GoogleSignIn
import UIKit

class ViewController: UIViewController {

  // Create an instance of the Sign in with Google button
  let signInButton = GIDSignInButton()
  let signOutButton = UIButton(type: .system)
  let welcomeLabel = UILabel()

  override func viewDidLoad() {
    super.viewDidLoad()

    // Set the width and color of the sign-in button
    signInButton.style = .standard  // Options: .standard, .wide, .iconOnly
    signInButton.colorScheme = .dark  // Options: .dark, .light

    // Add the sign-in button to your view
    view.addSubview(signInButton)

    // Position the button using constraints
    signInButton.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signInButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    ])

    // Add a target to the button to call a method when it's pressed
    signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)

    // --- Set up the Welcome Label ---
    welcomeLabel.translatesAutoresizingMaskIntoConstraints = false
    welcomeLabel.textAlignment = .center
    welcomeLabel.font = .systemFont(ofSize: 24, weight: .bold)
    view.addSubview(welcomeLabel)

    NSLayoutConstraint.activate([
      welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      welcomeLabel.bottomAnchor.constraint(equalTo: signInButton.topAnchor, constant: -20),
    ])

    // --- Set up the Sign-Out Button ---
    signOutButton.translatesAutoresizingMaskIntoConstraints = false
    signOutButton.setTitle("Sign Out", for: .normal)
    view.addSubview(signOutButton)

    NSLayoutConstraint.activate([
      signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signOutButton.topAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 20),
    ])

    signOutButton.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)

    // --- Set Initial UI State ---
    updateUI(for: nil)

    // Attempt to restore a previous sign-in session
    GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
      if let user = user {
        print("Successfully restored sign-in for user: \(user.profile?.givenName ?? "Unknown")")

        // Print the ID token when a session is restored.
        print("Restored ID Token: \(user.idToken?.tokenString ?? "")")

        // On success, update the UI for the signed-in state on the main thread.
        DispatchQueue.main.async {
          self.updateUI(for: user)
        }
      }
    }
  }

  // This method is called when the sign-in button is pressed.
  @objc func signInButtonTapped() {
    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      // If sign in succeeded, print the ID token.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")

      DispatchQueue.main.async {
        self.updateUI(for: result.user)
      }
    }
  }

  private func updateUI(for user: GIDGoogleUser?) {
    if let user = user {
      // User is signed in.
      signInButton.isHidden = true
      signOutButton.isHidden = false
      welcomeLabel.isHidden = false
      welcomeLabel.text = "Hello, \(user.profile?.givenName ?? "User")!"
    } else {
      // User is signed out.
      signInButton.isHidden = false
      signOutButton.isHidden = true
      welcomeLabel.isHidden = true
    }
  }

  @objc func signOutButtonTapped() {
    GIDSignIn.sharedInstance.signOut()
    // Update the UI for the signed-out state.
    updateUI(for: nil)
  }
}

Testar o login silencioso

Depois de fazer login, saia completamente do app e inicie-o novamente. Você vai notar que o login foi feito automaticamente, sem precisar tocar no botão.

10. Entender o token de ID

Embora o objeto GIDGoogleUser seja conveniente para personalizar a interface usando o nome e o e-mail do usuário, a parte mais importante dos dados retornados pelo SDK é o token de ID.

Este codelab usa uma ferramenta on-line para inspecionar o conteúdo do JWT. Em um app de produção, envie esse token de ID para o servidor de back-end. Seu servidor precisa verificar a integridade do token de ID e usar o JWT para fazer algo mais significativo, como criar uma nova conta na plataforma de back-end ou estabelecer uma nova sessão para o usuário.

Acessar e decodificar o token JWT

  1. Inicie o app.
  2. Abra o console do Xcode. Você vai encontrar um token de ID impresso. Ele vai ficar parecido com eyJhbGciOiJSUzI1Ni ... Hecz6Wm4Q.
  3. Copie o token de ID e use uma ferramenta on-line, como jwt.io (em inglês), para decodificar o JWT.

O JWT decodificado vai ficar assim:

{
  "alg": "RS256",
  "kid": "c8ab71530972bba20b49f78a09c9852c43ff9118",
  "typ": "JWT"
}
{
  "iss": "https://accounts.google.com",
  "azp": "171291171076-rrbkcjrp5jbte92ai9gub115ertscphi.apps.googleusercontent.com",
  "aud": "171291171076-rrbkcjrp5jbte92ai9gub115ertscphi.apps.googleusercontent.com",
  "sub": "10769150350006150715113082367",
  "email": "example@example.com",
  "email_verified": true,
  "at_hash": "JyCYDmHtzhjkb0-qJhKsMg",
  "name": "Kimya",
  "picture": "https://lh3.googleusercontent.com/a/ACg8ocIyy4VoR31t_n0biPVcScBHwZOCRaKVDb_MoaMYep65fyqoAw=s96-c",
  "given_name": "Kimya",
  "iat": 1758645896,
  "exp": 1758649496
}

Campos de token importantes

O token de ID decodificado contém campos com finalidades diferentes. Alguns são fáceis de entender, como nome e e-mail, enquanto outros são usados pelo servidor de back-end para verificação.

O campo a seguir é especialmente importante para entender:

  • sub: o campo sub é um identificador exclusivo e permanente da Conta do Google do usuário. Um usuário pode mudar o e-mail principal ou o nome, mas o ID do sub nunca muda. Isso torna o campo sub o valor perfeito para usar como uma chave primária para suas contas de usuário de back-end.

Obter informações do usuário do token de ID tem mais informações sobre o significado de todos os campos do token.

11. Proteja seu app com o App Check

É altamente recomendável ativar o App Check para garantir que apenas seu app possa acessar os endpoints OAuth 2.0 do Google em nome do seu projeto. O App Check funciona verificando se as solicitações para seus serviços de back-end se originam do seu app autêntico em um dispositivo real e não adulterado.

Esta seção mostra como integrar o App Check ao seu app e configurá-lo para depuração em um simulador e para um build de produção executado em um dispositivo real.

Configuração do console

A integração do App Check ao seu aplicativo exige uma configuração única nos consoles do Google Cloud e do Firebase. Isso envolve ativar o App Check para seu cliente OAuth do iOS no console do Google Cloud, criar uma chave de API para uso com o provedor de depuração do App Check e vincular seu projeto do Google Cloud ao Firebase.

Ativar o App Check no console do Google Cloud

  1. Navegue até a lista de clientes associados ao seu projeto do Google Cloud.
  2. Selecione o ID do cliente OAuth 2.0 que você criou para seu app iOS.
  3. Ative o App Check em Identidade do Google para iOS.

Página de edição do cliente OAuth com a chave do App Check

  1. Clique em Salvar.

Criar uma chave de API

  1. Acesse a página Biblioteca de APIs do seu projeto do Google Cloud.
  2. Digite API Firebase App Check na barra de pesquisa.

Página da biblioteca de APIs do console do Google Cloud

  1. Selecione e ative a API Firebase App Check.
  2. Acesse APIs e serviços e selecione Credenciais no menu de navegação.
  3. Selecione Criar credenciais na parte de cima da página.

Página de credenciais da API do console do Google Cloud

  1. Atribua um nome a essa chave de API.
  2. Selecione "Apps iOS" em Restrições de aplicativo.
  3. Adicione o identificador do pacote do seu app como um aplicativo aprovado.
  4. Selecione Restringir chave em Restrições de API.
  5. Selecione API Firebase App Check no menu suspenso.
  6. Selecione Criar.

Página de criação de chaves de API do Console do Google Cloud

  1. Copie a chave de API criada. Você vai precisar dele em outra etapa.

Adicione o Firebase ao projeto do Google Cloud

  1. Navegue até o Console do Firebase.
  2. Selecione Comece configurando um projeto do Firebase.
  3. Selecione Adicionar o Firebase ao projeto do Google Cloud.

Adicionar o Firebase a um projeto do Google Cloud

  1. Selecione um projeto do Google Cloud no menu suspenso e continue o fluxo de inscrição.
  2. Selecione Adicionar Firebase.
  3. Quando o projeto do Firebase estiver pronto, selecione Continuar para abrir o projeto.

Integração de código do lado do cliente

Com o projeto do Google Cloud configurado para o App Check, é hora de escrever o código do lado do cliente para ativar o serviço. O provedor usado para atestado é diferente nos ambientes de produção e de depuração. Um app de produção em um dispositivo real usa o serviço App Attest integrado da Apple para provar sua autenticidade. No entanto, como o simulador do iOS não pode fornecer esse tipo de declaração, o ambiente de depuração exige um provedor de depuração especial que recebe uma chave de API.

O código a seguir processa os dois cenários usando uma diretiva do compilador para selecionar automaticamente o provedor correto no momento da build.

SwiftUI

  1. Abra o arquivo principal do app.
  2. Defina a seguinte classe AppDelegate após as importações e antes do atributo @main:
class AppDelegate: NSObject, UIApplicationDelegate {
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {

    #if targetEnvironment(simulator)
      // Configure for debugging on a simulator.
      // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
      let apiKey = "YOUR_API_KEY"
      GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
        if let error {
          print("Error configuring GIDSignIn debug provider: \(error)")
        }
      }
    #else
      // Configure GIDSignIn for App Check on a real device.
      GIDSignIn.sharedInstance.configure { error in
        if let error {
          print("Error configuring GIDSignIn for App Check: \(error)")
        } else {
          print("GIDSignIn configured for App Check.")
        }
      }
    #endif

    return true
  }
}
  1. Substitua "YOUR_API_KEY" no código fornecido pela chave de API que você copiou do console do Google Cloud.
  2. Adicione a seguinte linha dentro da sua estrutura App, logo antes da variável body. Isso registra a classe AppDelegate com o ciclo de vida do app, permitindo que ela responda ao lançamento do app e a outros eventos do sistema:
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

O arquivo principal do app vai ficar assim:

import GoogleSignIn
import SwiftUI

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

    #if targetEnvironment(simulator)
      // Configure for debugging on a simulator.
      // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
      let apiKey = "YOUR_API_KEY"
      GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
        if let error {
          print("Error configuring GIDSignIn debug provider: \(error)")
        }
      }
    #else
      // Configure GIDSignIn for App Check on a real device.
      GIDSignIn.sharedInstance.configure { error in
        if let error {
          print("Error configuring GIDSignIn for App Check: \(error)")
        } else {
          print("GIDSignIn configured for App Check.")
        }
      }
    #endif

    return true
  }
}

@main
struct iOS_Sign_in_with_Google_App: App {

  @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

  var body: some Scene {
    WindowGroup {
      ContentView()

        .onOpenURL { url in
          GIDSignIn.sharedInstance.handle(url)
        }
    }
  }
}

UIKit

  1. Abra AppDelegate.swift.
  2. Atualize o método application(_:didFinishLaunchingWithOptions:) para incluir a inicialização do App Check:
func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {

  #if targetEnvironment(simulator)
    // Configure for debugging on a simulator.
    // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
    let apiKey = "YOUR_API_KEY"
    GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
      if let error {
        print("Error configuring GIDSignIn debug provider: \(error)")
      }
    }
  #else
    // Configure GIDSignIn for App Check on a real device.
    GIDSignIn.sharedInstance.configure { error in
      if let error {
        print("Error configuring GIDSignIn for App Check: \(error)")
      }
    }
  #endif

  return true
}
  1. Substitua "YOUR_API_KEY" no código fornecido pela chave de API que você copiou do console do Google Cloud.

O arquivo AppDelegate.swift vai ficar assim:

import GoogleSignIn
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

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

    #if targetEnvironment(simulator)
      // Configure for debugging on a simulator.
      // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
      let apiKey = "YOUR_API_KEY"
      GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
        if let error {
          print("Error configuring GIDSignIn debug provider: \(error)")
        }
      }
    #else
      // Configure GIDSignIn for App Check on a real device.
      GIDSignIn.sharedInstance.configure { error in
        if let error {
          print("Error configuring GIDSignIn for App Check: \(error)")
        }
      }
    #endif

    return true
  }

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

    handled = GIDSignIn.sharedInstance.handle(url)
    if handled {
      return true
    }
    // If not handled by this app, return false.
    return false
  }

  // MARK: UISceneSession Lifecycle

  func application(
    _ application: UIApplication,
    configurationForConnecting connectingSceneSession: UISceneSession,
    options: UIScene.ConnectionOptions
  ) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return UISceneConfiguration(
      name: "Default Configuration",
      sessionRole: connectingSceneSession.role
    )
  }

  func application(
    _ application: UIApplication,
    didDiscardSceneSessions sceneSessions: Set<UISceneSession>
  ) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
  }
}

Testar o App Check no simulador

  1. Na barra de menus do Xcode, navegue até Product > Scheme > Edit Scheme.
  2. Selecione Executar no menu de navegação.
  3. Selecione a guia Arguments.
  4. Na seção Argumentos transmitidos na inicialização, selecione + e adicione -FIRDebugEnabled. Esse argumento de inicialização ativa o registro de depuração do Firebase.
  5. Selecione Fechar.

Página do editor de argumentos do Xcode

  1. Inicie o app no simulador.
  2. Copie o token de depuração do App Check impresso no console do Xcode.

Token de depuração do App Check no console do Xcode

  1. Acesse seu projeto no Console do Firebase.
  2. Expanda a seção Build no menu de navegação.
  3. Selecione App Check.
  4. Selecione a guia Apps.
  5. Passe o cursor sobre o app e selecione o ícone do menu de três pontos.

Configurações do Firebase App Check

  1. Selecione Gerenciar tokens de depuração.
  2. Selecione Adicionar token de depuração.
  3. Dê um nome ao token de depuração e cole o token que você copiou antes como valor.
  4. Selecione Salvar para registrar seu token.

Gerenciamento de tokens de depuração do Firebase App Check

  1. Volte ao simulador e faça login.

Pode levar vários minutos para que as métricas apareçam no console. Depois disso, você pode confirmar se o App Check está funcionando procurando um aumento nas solicitações Verificadas em um destes dois lugares:

  • Na seção "App Check" do console do Firebase, na guia "APIs".

Métricas do Firebase App Check

  • Na página de edição do cliente OAuth no console do Google Cloud.

Métricas do App Check no Console do Google Cloud

Depois de monitorar as métricas do App Check do seu app e confirmar que as solicitações legítimas estão sendo verificadas, ative a aplicação do App Check. Depois de aplicado, o App Check rejeita todas as solicitações não verificadas, garantindo que apenas o tráfego do seu app autêntico possa acessar os endpoints OAuth 2.0 do Google em nome do seu projeto.

12. Outros recursos

Parabéns!

Você configurou um cliente iOS do OAuth 2.0, adicionou um botão "Fazer login com o Google" a um app iOS, aprendeu a personalizar a aparência do botão, decodificou um token de ID JWT e ativou o App Check para seu app.

Estes links podem ajudar com as próximas etapas:

Perguntas frequentes