1. Прежде чем начать
В этой лабораторной работе вы научитесь создавать iOS-приложение, реализующее функцию «Вход через Google» и работающее в симуляторе. Реализации реализованы как на SwiftUI, так и на UIKit.
SwiftUI — это современный UI-фреймворк Apple для разработки новых приложений. Он позволяет создавать пользовательские интерфейсы для всех платформ Apple на основе единой кодовой базы. Требуется iOS версии не ниже 13.
UIKit — оригинальный и основополагающий UI-фреймворк Apple для iOS. Он обеспечивает обратную совместимость со старыми версиями iOS. Это делает его хорошим выбором для уже существующих приложений, которым требуется поддержка различных старых устройств.
Вы можете следовать по пути выбора фреймворка, который наилучшим образом соответствует вашим потребностям в разработке.
Предпосылки
Чему вы научитесь
- Как создать проект Google Cloud
- Как создать клиентов OAuth в Google Cloud Console
- Как реализовать функцию «Войти через Google» в вашем приложении iOS
- Как настроить кнопку «Войти через Google»
- Как декодировать идентификационный токен
- Как включить проверку приложений для вашего приложения iOS
Что вам понадобится
- Текущая версия Xcode
- Компьютер под управлением macOS, соответствующий системным требованиям установленной версии Xcode.
Эта лабораторная работа была создана в Xcode 16.3 с использованием симулятора iOS 18.3. Для разработки используйте последнюю версию Xcode.
2. Создайте новый проект Xcode.
- Откройте Xcode и выберите Создать новый проект Xcode .
- Выберите вкладку iOS , выберите шаблон приложения и нажмите Далее .

- В вариантах проекта:
- Введите название вашего продукта .
- При желании выберите свою команду .
- Введите идентификатор вашей организации .
- Запишите сгенерированный идентификатор пакета . Он понадобится вам позже.
- Для Интерфейса выберите один из вариантов:
- SwiftUI для приложения на базе SwiftUI.
- Раскадровка для приложения на базе UIKit.
- Выберите Swift в качестве языка .
- Нажмите «Далее» и выберите место для сохранения вашего проекта.

3. Создайте OAuth-клиент
Чтобы ваше приложение могло взаимодействовать со службами аутентификации Google, вам необходимо создать идентификатор клиента OAuth. Для этого потребуется проект Google Cloud. Следующие шаги помогут вам создать проект и идентификатор клиента OAuth.
Выберите или создайте проект Google Cloud
- Перейдите в Google Cloud Console и выберите или создайте проект. Если вы выбираете существующий проект, консоль автоматически перенаправит вас к следующему необходимому шагу.

- Введите имя для вашего нового проекта Google Cloud.
- Выберите Создать .

Настройте экран согласия
Если вы уже настроили экран согласия для выбранного проекта, вам не будет предложено настроить его сейчас. В этом случае вы можете пропустить этот раздел и перейти к разделу «Создание клиента OAuth 2.0» .
- Выберите Настроить экран согласия .

- Выберите « Начать» на странице брендинга.

- На странице конфигурации проекта заполните следующие поля:
- Информация о приложении : введите название и адрес электронной почты службы поддержки пользователей вашего приложения. Этот адрес будет доступен всем пользователям, чтобы они могли связаться с вами по вопросам, касающимся их согласия.
- Аудитория : выберите Внешняя .
- Контактная информация : введите адрес электронной почты, по которому Google сможет связаться с вами по поводу вашего проекта.
- Ознакомьтесь с политикой использования пользовательских данных в разделе «Сервисы API Google».
- Нажмите «Создать» .

- Выберите страницу «Клиенты» в навигационном меню.
- Нажмите «Создать клиента» .

Создайте клиент OAuth 2.0
- Выберите iOS в качестве типа приложения .
- Введите имя вашего клиента.
- Введите идентификатор пакета, созданный на последнем шаге.
- Введите идентификатор команды , назначенный вашей команде Apple. Этот шаг пока необязателен, но он потребуется для включения проверки приложений в ходе выполнения этой практической работы.
- Выберите Создать .

- Скопируйте идентификатор клиента из диалогового окна, он понадобится вам позже.
- Загрузите файл plist для дальнейшего использования.

4. Настройте свой проект Xcode
Следующий шаг — настроить проект Xcode для работы с SDK «Вход через Google». Этот процесс включает добавление SDK в ваш проект в качестве зависимости и настройку параметров проекта с использованием уникального идентификатора клиента. Этот идентификатор позволяет SDK безопасно взаимодействовать со службой аутентификации Google во время входа в систему.
Установите зависимости «Войти через Google»
- Откройте ваш проект Xcode.
- Перейдите в Файл > Добавить зависимости пакета .
- В строке поиска введите URL-адрес репозитория «Войти через Google»: https://github.com/google/GoogleSignIn-iOS

- Выберите Добавить пакет .
- Выберите основную цель приложения для пакета GoogleSignIn .
- Если вы используете SwiftUI, выберите основное целевое приложение для пакета GoogleSignInSwift . Если вы планируете использовать UIKit, не выбирайте целевое приложение для этого пакета.
- Выберите Добавить пакет .

Настройте учетные данные вашего приложения
- В навигаторе проектов щелкните корень вашего проекта.
- В основной области редактора выберите основную цель приложения из списка ЦЕЛИ .
- Выберите вкладку «Информация» в верхней части области редактора.
- Наведите указатель мыши на последнюю строку в разделе «Свойства пользовательского объекта iOS» и нажмите появившуюся кнопку «+» .

- В столбце «Ключ» введите GIDClientID.
- В столбце «Значение» вставьте идентификатор клиента, скопированный из Google Cloud Console.

- Откройте файл plist , загруженный из Google Cloud Console.
- Скопируйте значение для обратного идентификатора клиента .

- Разверните Типы URL в нижней части вкладки Информация .
- Нажмите кнопку + .
- Введите обратный идентификатор клиента в поле Схемы URL .

Теперь мы готовы приступить к добавлению кнопки входа в наше приложение!
5. Добавьте кнопку входа.
После настройки проекта Xcode пришло время добавить в приложение кнопку «Войти через Google»!
Основная логика этого шага — вызов GIDSignIn.sharedInstance.signIn . Этот метод запускает процесс аутентификации, передавая управление SDK «Войти через Google» для отображения пользователю процесса входа через Google.
SwiftUI
- Найдите файл ContentView.swift в навигаторе проектов Xcode.
- Замените содержимое этого файла следующим текстом:
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()
}

UIKit
- Найдите файл ViewController.swift в навигаторе проектов Xcode.
- Замените содержимое этого файла следующим текстом:
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 ?? "")")
}
}
}

Просмотреть кнопку входа
Запустите приложение в симуляторе. Вы увидите кнопку «Войти через Google», но пока она не будет работать корректно. Это ожидаемо, так как вам ещё нужно реализовать код для обработки перенаправления обратно в приложение после аутентификации пользователя.
6. Настройте кнопку входа
Вы можете настроить кнопку «Войти через Google» по умолчанию, чтобы она лучше соответствовала теме вашего приложения. SDK «Войти через Google» позволяет изменить цветовую схему и стиль кнопки.
SwiftUI
Кнопка по умолчанию добавляется на страницу с помощью этой строки кода:
GoogleSignInButton(action: handleSignInButton)
Кнопка GoogleSignInButton настраивается путём передачи параметров её инициализатору. Следующий код добавит кнопке входа тёмную тему.
- Открыть ContentView.swift
- Обновите инициализатор
GoogleSignInButton, добавив в него следующие значения:
GoogleSignInButton(
scheme: .dark, // Options: .light, .dark, .auto
style: .standard, // Options: .standard, .wide, .icon
state: .normal, // Options: .normal, .disabled
action: handleSignInButton
).padding()

Более подробную информацию о параметрах настройки см. в справочнике по фреймворку GoogleSignInSwift.
UIKit
Кнопка по умолчанию создается с помощью следующих строк кода:
// Create an instance of the Sign in with Google button
let signInButton = GIDSignInButton()
// Add the button to your view
view.addSubview(signInButton)
GIDSignInButton настраивается путём установки свойств экземпляра кнопки. Следующий код добавит кнопке входа тёмную тему.
- Откройте ViewController.swift .
- Добавьте следующие строки кода непосредственно перед добавлением кнопки входа в представление в функции
viewDidLoad:
// Set the width and color of the sign-in button
signInButton.style = .standard // Options: .standard, .wide, .iconOnly
signInButton.colorScheme = .dark // Options: .dark, .light

Более подробную информацию о настройке см. в справочнике по фреймворку GoogleSignIn.
7. Обработайте URL-адрес перенаправления аутентификации.
После добавления кнопки входа следующим шагом станет обработка перенаправления, которое происходит после аутентификации пользователя. После аутентификации Google возвращает URL с временным кодом авторизации. Для завершения процесса входа обработчик перехватывает этот URL и передает его в SDK «Войти через Google» для обмена на подписанный токен ID (JWT).
SwiftUI
- Откройте файл, содержащий структуру вашего
App. Имя файла соответствует названию вашего проекта, поэтому оно будет выглядеть примерно так: YourProjectNameApp.swift . - Замените содержимое этого файла следующим текстом:
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
- Откройте AppDelegate.swift .
- Добавьте следующий импорт в начало файла:
import GoogleSignIn
- Добавьте следующую функцию-обработчик аутентификации в класс
AppDelegate. Лучше всего разместить её сразу после закрывающей скобки метода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
}
После внесения этих изменений ваш файл AppDelegate.swift должен выглядеть следующим образом:
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.
}
}
Протестируйте процесс входа в систему
Теперь вы можете протестировать полный процесс входа в систему!
Запустите приложение и нажмите кнопку входа. После аутентификации Google откроет экран согласия, где вы сможете предоставить приложению доступ к вашим данным. После подтверждения вход будет завершён, и вы вернётесь в приложение.
После успешного входа SDK «Вход с Google» безопасно сохраняет учётные данные пользователя в связке ключей устройства. Эти данные можно использовать позже, чтобы пользователь оставался в системе при последующих запусках приложения.
8. Добавьте кнопку выхода
Теперь, когда вход работает, следующим шагом будет добавление кнопки выхода и обновление пользовательского интерфейса с учётом текущего состояния входа пользователя. При успешном входе SDK предоставляет объект GIDGoogleUser . Этот объект содержит свойство profile с базовой информацией, такой как имя пользователя и адрес электронной почты, которую вы будете использовать для персонализации пользовательского интерфейса.
SwiftUI
- Откройте файл ContentView.swift .
- Добавьте переменную состояния в начало структуры
ContentView. Эта переменная будет хранить информацию о пользователе после входа в систему. Поскольку это переменная@State, SwiftUI будет автоматически обновлять ваш пользовательский интерфейс при каждом изменении её значения:
struct ContentView: View {
@State private var user: GIDGoogleUser?
}
- Замените текущее
bodyструктурыContentViewследующимVStack. Это проверит, содержит ли переменная состоянияuserимя пользователя. Если да, будет показано приветственное сообщение и кнопка выхода. Если нет, будет отображена исходная кнопка «Войти через Google»:
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()
}
}
}
- Обновите блок автодополнения
handleSignInButton, чтобы присвоитьsignInResult.userновой переменнойuser. Это запустит переключение пользовательского интерфейса в режим входа в систему:
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 ?? "")")
}
}
- Добавьте новую функцию
signOutв конец структурыContentView, которая будет вызываться кнопкой выхода:
func signOut() {
GIDSignIn.sharedInstance.signOut()
// After signing out, set the `user` state variable to `nil`.
self.user = nil
}
Запустите приложение и войдите в систему. После успешной аутентификации вы увидите изменение пользовательского интерфейса!

После внесения этих изменений ваш файл ContentView.swift должен выглядеть следующим образом:
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
- Откройте ViewController.swift .
- В верхней части
ViewController, прямо под тем местом, где вы объявилиsignInButton, добавьте кнопку выхода и приветственную надпись:
let signOutButton = UIButton(type: .system)
let welcomeLabel = UILabel()
- Добавьте следующую функцию в нижнюю часть
ViewController. Эта функция будет отображать пользователю разный пользовательский интерфейс в зависимости от его статуса входа:
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
}
}
- В нижней части функции
viewDidLoadдобавьте следующий код, чтобы добавить приветственную надпись и кнопку выхода в представление:
// --- 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)
- Обновите функцию
signInButtonTappedдля вызова методаUpdateUIпри успешном входе в систему:
@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)
}
}
}
- Наконец, добавьте функцию
signOutButtonTappedвViewControllerдля управления процессом выхода из системы:
@objc func signOutButtonTapped() {
GIDSignIn.sharedInstance.signOut()
// Update the UI for the signed-out state.
updateUI(for: nil)
}
Запустите приложение и войдите в систему. После успешной аутентификации вы увидите изменение пользовательского интерфейса!

После внесения этих изменений ваш файл ViewController.swift должен выглядеть следующим образом:
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. Восстановите состояние входа пользователя в систему
Чтобы улучшить взаимодействие с возвращающимися пользователями, следующим шагом будет восстановление состояния входа при запуске приложения. Вызов restorePreviousSignIn использует учётные данные, сохранённые в Keychain, для автоматического повторного входа пользователя, избавляя его от необходимости каждый раз проходить процедуру входа.
SwiftUI
- Откройте ContentView.swift .
- Добавьте следующий код непосредственно после
VStackвнутри переменной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 ?? "")")
}
}
}
Ваш ContentView.swift должен выглядеть так:
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
- Откройте ViewController.swift .
- Добавьте следующий вызов
restorePreviousSignInв конец метода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)
}
}
}
Ваш файл ViewController.swift должен выглядеть так:
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)
}
}
Протестируйте тихий вход
После входа полностью закройте приложение и запустите его снова. Вы увидите, что теперь вход выполнен автоматически, без необходимости нажимать кнопку.
10. Понимание идентификатора токена
Хотя объект GIDGoogleUser удобен для персонализации пользовательского интерфейса с использованием имени и адреса электронной почты пользователя, наиболее важным фрагментом данных, возвращаемым из SDK, является идентификационный токен.
В этой лабораторной работе используется онлайн-инструмент для проверки содержимого JWT. В рабочем приложении вам следует отправить этот ID-токен на ваш бэкенд-сервер. Ваш сервер должен проверить целостность ID-токена и использовать JWT для выполнения более значимых задач, например, для создания новой учётной записи на вашей бэкенд-платформе или для установки нового сеанса для пользователя.
Доступ к токену JWT и его декодирование
- Запустите приложение.
- Откройте консоль Xcode. Вы должны увидеть распечатанный идентификатор токена. Он будет выглядеть примерно так:
eyJhbGciOiJSUzI1Ni ... Hecz6Wm4Q. - Скопируйте идентификатор токена и используйте онлайн-инструмент, такой как jwt.io , для декодирования JWT.
Декодированный JWT будет выглядеть так:
{
"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
}
Известные поля токенов
Декодированный идентификатор токена содержит поля с разными функциями. Некоторые из них, например, имя и адрес электронной почты, легко распознаются, другие используются вашим внутренним сервером для проверки.
Особенно важно понимать следующее поле:
- sub : поле
sub— это уникальный постоянный идентификатор учётной записи Google пользователя. Пользователь может изменить свой основной адрес электронной почты или имя, но егоsubидентификатор останется неизменным. Это делает полеsubидеальным значением для использования в качестве первичного ключа для учётных записей внутренних пользователей.
Получить информацию о пользователе из идентификатора токена — более подробная информация о значении всех полей токена.
11. Защитите свое приложение с помощью App Check
Настоятельно рекомендуется включить функцию App Check, чтобы гарантировать, что доступ к конечным точкам Google OAuth 2.0 от имени вашего проекта будет иметь только ваше приложение. App Check проверяет, что запросы к вашим бэкэнд-сервисам исходят от вашего подлинного приложения на реальном и нетронутом устройстве.
В этом разделе показано, как интегрировать App Check в ваше приложение и настроить его как для отладки в симуляторе, так и для производственной сборки, работающей на реальном устройстве.
Настройка консоли
Для интеграции App Check в ваше приложение требуется однократная настройка в консолях Google Cloud и Firebase. Это включает в себя включение App Check для вашего OAuth-клиента iOS в консоли Google Cloud, создание ключа API для использования с поставщиком отладки App Check и привязку вашего проекта Google Cloud к Firebase.
Включить проверку приложений в Google Cloud Console
- Перейдите к списку клиентов , связанных с вашим проектом Google Cloud.
- Выберите идентификатор клиента OAuth 2.0, созданный для вашего приложения iOS.
- Включите проверку приложений в разделе «Идентификация Google» для iOS.

- Нажмите «Сохранить» .
Создать ключ API
- Перейдите на страницу библиотеки API для вашего проекта Google Cloud.
- Введите Firebase App Check API в строку поиска.

- Выберите и включите Firebase App Check API .
- Перейдите в раздел API и службы и выберите Учетные данные в навигационном меню.
- Выберите Создать учетные данные в верхней части страницы.

- Присвойте имя этому API-ключу.
- Выберите приложения iOS в разделе «Ограничения приложений» .
- Добавьте идентификатор пакета для вашего приложения как одобренного приложения.
- Выберите Ограничить ключ в разделе Ограничения API .
- В раскрывающемся меню выберите Firebase App Check API .
- Выберите Создать .

- Скопируйте созданный ключ API. Он понадобится вам на следующем этапе.
Добавьте Firebase в свой проект Google Cloud
- Перейдите в консоль Firebase .
- Выберите Начать с настройки проекта Firebase .
- Выберите Добавить Firebase в проект Google Cloud .

- Выберите проект Google Cloud из раскрывающегося списка и продолжите процесс регистрации.
- Выберите Добавить Firebase .
- Когда ваш проект Firebase будет готов, нажмите Продолжить , чтобы открыть проект.
Интеграция клиентского кода
После настройки проекта Google Cloud для App Check пришло время написать клиентский код для его включения. Поставщик, используемый для аттестации, различается в рабочей и отладочной средах. Производственное приложение на реальном устройстве использует встроенный сервис Apple App Attest для подтверждения своей подлинности. Однако, поскольку симулятор iOS не может обеспечить такую аттестацию, для отладочной среды требуется специальный поставщик отладки, которому передается ключ API.
Следующий код обрабатывает оба сценария, используя директиву компилятора для автоматического выбора правильного поставщика во время сборки.
SwiftUI
- Откройте основной файл приложения.
- Определите следующий класс
AppDelegateпосле импорта и перед атрибутом@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
}
}
- Замените
"YOUR_API_KEY"в предоставленном коде на ключ API, скопированный из Google Cloud Console. - Добавьте следующую строку в структуру
App, прямо перед переменнойbody. Это зарегистрирует ваш класс AppDelegate в жизненном цикле приложения, что позволит ему реагировать на запуск приложения и другие системные события:
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
Ваш основной файл приложения должен выглядеть так:
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
- Откройте AppDelegate.swift .
- Обновите метод
application(_:didFinishLaunchingWithOptions:), чтобы он содержал инициализацию 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
}
- Замените
"YOUR_API_KEY"в предоставленном коде на ключ API, скопированный из Google Cloud Console.
Ваш файл AppDelegate.swift должен выглядеть так:
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.
}
}
Тестовое приложение. Проверка на симуляторе.
- В строке меню Xcode выберите Продукт > Схема > Редактировать схему .
- В навигационном меню выберите пункт Выполнить .
- Выберите вкладку «Аргументы» .
- В разделе «Аргументы, передаваемые при запуске» выберите + и добавьте -FIRDebugEnabled . Этот аргумент запуска включает ведение журнала отладки Firebase.
- Выберите Закрыть .

- Запустите свое приложение на симуляторе.
- Скопируйте токен отладки App Check , который выводится в консоли Xcode.

- Перейдите к своему проекту в консоли Firebase .
- Разверните раздел «Сборка» в навигационном меню.
- Выберите «Проверка приложений» .
- Выберите вкладку «Приложения» .
- Наведите указатель мыши на свое приложение и выберите значок меню с тремя точками.

- Выберите Управление токенами отладки .
- Выберите Добавить токен отладки .
- Дайте вашему токену отладки имя и вставьте скопированный ранее токен отладки в качестве значения.
- Нажмите «Сохранить» , чтобы зарегистрировать токен.

- Вернитесь в симулятор и войдите в систему.
Отображение метрик в консоли может занять несколько минут. После этого вы можете убедиться в работоспособности App Check, обратив внимание на увеличение количества проверенных запросов в одном из двух мест:
- В разделе «Проверка приложений» консоли Firebase, на вкладке «API».

- На странице редактирования вашего клиента OAuth в Google Cloud Console.

После мониторинга показателей App Check вашего приложения и подтверждения того, что легитимные запросы проверяются, следует включить принудительное применение App Check . После включения App Check отклоняет все непроверенные запросы, гарантируя, что доступ к конечным точкам OAuth 2.0 Google от имени вашего проекта будет получать только трафик из вашего подлинного приложения.
12. Дополнительные ресурсы
Поздравляю!
Вы настроили клиент OAuth 2.0 iOS, добавили кнопку «Войти через Google» в приложение iOS, узнали, как настраивать внешний вид кнопки, декодировали токен JWT ID и включили проверку приложений для своего приложения.
Эти ссылки могут помочь вам с дальнейшими шагами:
- Как начать работу с Google Sign-In для iOS
- Репозиторий Google Sign-In для iOS
- Проверьте токен Google ID
- Начните использовать App Check для входа в Google на iOS
- Отзыв токенов доступа и отключение приложения
- Узнайте больше о проектах Google Cloud
- Методы аутентификации Google Identity
- Включить принудительное выполнение проверки приложений