
FirebaseUI для SwiftUI – это современная библиотека, созданная на основе Firebase Authentication и позволяющая добавлять в приложение готовые процессы входа.
FirebaseUI для SwiftUI имеет следующие преимущества:
- Готовый интерфейс по умолчанию. Добавьте полный процесс входа с помощью
AuthPickerView. - Настраиваемость. Вы можете использовать стандартные кнопки в собственном макете или создать полностью настраиваемый интерфейс.
- Связывание анонимных аккаунтов. Вы можете не заменять анонимных пользователей, а преобразовывать их в обычные.
- Управление аккаунтом. Встроенные процессы регистрации, восстановления пароля и управления аккаунтом.
- Несколько поставщиков: адрес электронной почты и пароль, ссылка в электронном письме, аутентификация по телефону, Apple, Google, Facebook, Twitter и стандартные поставщики OAuth2/OIDC.
- Современные функции аутентификации. Встроенная поддержка многофакторной аутентификации (МФА) и API async/await.
Подготовка
Добавьте Firebase в проект Apple. Обязательно выполните шаги SwiftUI.
Убедитесь, что ваше приложение предназначено для iOS 17 или более поздней версии.
Установка
FirebaseUI для SwiftUI предоставляется в виде пакета Swift. Чтобы установить пакет в проект Xcode, выполните следующие действия:
В Xcode нажмите File (Файл) > Add Package Dependencies (Добавить зависимости пакетов).
Введите URL пакета:
https://github.com/firebase/FirebaseUI-iOSВ меню Dependency Rule (Правило зависимости) выберите Up to Next Major Version (До следующей основной версии) и укажите в качестве минимальной версии последний выпуск.
Нажмите Add Package (Добавить пакет) и выберите библиотеки, которые нужно добавить в проект.
Следующая библиотека требуется всегда: Он включает основные зависимости, а также поддержку входа с помощью пароля и ссылки, отправленной по электронной почте.
FirebaseAuthSwiftUI
Если вы хотите поддерживать другие способы входа, выберите одну или несколько из следующих библиотек:
FirebaseAppleSwiftUI(Sign in with Apple)FirebaseGoogleSwiftUI(Вход с аккаунтом Google)FirebaseFacebookSwiftUI(Войти с аккаунтом Facebook)FirebasePhoneAuthSwiftUI(аутентификация по телефону)FirebaseTwitterSwiftUI(Sign in with Twitter)FirebaseOAuthSwiftUI(стандартные поставщики OAuth и OIDC, такие как GitHub, Microsoft, Yahoo)
Нажмите Add Package (Добавить пакет), чтобы установить выбранные библиотеки.
Настройте
AuthServiceFirebaseUI в инициализаторе верхнего уровняViewи передайте сервис дочерним представлениям, используя среду.import FirebaseAuthSwiftUI import SwiftUI struct ContentView: View { let authService: AuthService init() { let configuration = AuthConfiguration() authService = AuthService(configuration: configuration) .withEmailSignIn() // Or whatever sign-in methods you want to support. // See the next section. } var body: some View { AuthPickerView { // AuthPickerView (the prebuilt View) or a custom View. Text("Welcome to your app!") } .environment(authService) } }
Как настроить способы входа
Для каждого из поддерживаемых способов входа требуются дополнительные действия по настройке. Разверните разделы ниже и следуйте инструкциям по настройке отдельных поставщиков.
Адрес электронной почты и пароль
В разделе Security (Безопасность) > Authentication (Аутентификация) > Sign-in method (Способ входа) консоли Firebase включите поставщика Email/Password (Адрес электронной почты/пароль).
Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withEmailSignIn()
Аутентификация по ссылке в электронном письме
Чтобы использовать вход по ссылке в электронном письме без пароля:
В разделе Security (Безопасность) > Authentication (Аутентификация) > Sign-in method (Способ входа) консоли Firebase включите параметр Email/Password (Адрес электронной почты/пароль), а затем – вход по ссылке в электронном письме. Чтобы использовать вход по ссылке в электронном письме, необходимо включить вход по адресу электронной почты или паролю.
Добавьте домен ссылки в Авторизованные домены.
Настройте
ActionCodeSettings, передайте его вAuthConfigurationи зарегистрируйте поставщика:let actionCodeSettings = ActionCodeSettings() actionCodeSettings.handleCodeInApp = true actionCodeSettings.url = URL(string: "https://yourapp.firebaseapp.com") guard let bundleID = Bundle.main.bundleIdentifier else { fatalError("Missing bundle identifier for email link authentication setup.") } actionCodeSettings.setIOSBundleID(bundleID) let configuration = AuthConfiguration( emailLinkSignInActionCodeSettings: actionCodeSettings ) let authService = AuthService(configuration: configuration) .withEmailLinkSignIn()В том же классе
AppDelegate, в котором вы вызываетеFirebaseApp.configure(), добавьте логику в методapplication(_:open:options:), который возвращаетtrue, когда открытый URL является ссылкой для входа в Firebase Authentication. Эта логика указывает, что ссылка была обработана FirebaseUI и не должна обрабатываться другими обработчиками ссылок.import FacebookCore import FirebaseAuth import UIKit class AppDelegate: NSObject, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { FirebaseApp.configure() return true } func application( _ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] ) -> Bool { if Auth.auth().canHandle(url) { return true } return false } }Если вы создаете собственные представления, вызывайте
authService.handleSignInLink(url:), когда ссылка открывает ваше приложение.
Apple
Чтобы использовать функцию "Войти с Apple":
Настройте функцию "Войти с Apple":
- Включите функцию Sign In with Apple для своего приложения на странице Certificates, Identifiers & Profiles (Сертификаты, идентификаторы и профили) на сайте Apple для разработчиков.
- Свяжите сайт с приложением, как описано в первом разделе статьи Configure Sign In with Apple for the web (Как настроить функцию "Войти с Apple" для сайта). Когда появится запрос, зарегистрируйте следующий URL в качестве URL возврата:
Идентификатор проекта Firebase можно найти на странице настроек консоли Firebase. Запишите идентификатор сервиса. Он понадобится вам в следующем разделе.https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
- Создайте закрытый ключ для функции "Войти с Apple". Новый закрытый ключ и его идентификатор понадобятся вам в следующем разделе.
- Если вы используете какие-либо функции Firebase Authentication, которые отправляют электронные письма пользователям, включая вход по ссылке в электронном письме, подтверждение адреса электронной почты, отмену изменения аккаунта и другие, настройте службу частной ретрансляции электронной почты Apple и зарегистрируйте
noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com(или ваш настроенный домен шаблона электронной почты), чтобы Apple могла ретранслировать электронные письма, отправленные Firebase Authentication, на анонимные адреса электронной почты Apple.
В разделе Security (Безопасность) > Authentication (Аутентификация) > Sign-in method (Способ входа) консоли Firebase включите Apple.
- Укажите идентификатор сервиса, созданный в предыдущем разделе.
- В разделе OAuth code flow configuration (Конфигурация потока кода OAuth) укажите идентификатор Apple Team ID, а также закрытый ключ и его идентификатор, созданные в предыдущем разделе.
В Xcode откройте раздел Signing & Capabilities (Подпись и возможности) в редакторе проекта и добавьте возможность Sign in with Apple (Вход с Apple).
Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withAppleSignIn()
Чтобы использовать вход с аккаунтом Google:
В разделе Security (Безопасность) > Authentication (Аутентификация) > Sign-in method (Способ входа) консоли Firebase включите поставщика Google.
Скачайте новую копию файла
GoogleService-Info.plistпроекта и скопируйте ее в проект Xcode. Перезапишите все существующие версии новой.Добавьте специальные схемы URL в проект Xcode:
Откройте конфигурацию проекта, нажав на его название в дереве аккаунта слева. Выберите приложение в разделе TARGETS (ЦЕЛИ), откройте вкладку Info (Информация) и разверните раздел URL Types (Типы URL).
Нажмите кнопку + и добавьте схему URL для обратного идентификатора клиента. Чтобы найти это значение, откройте файл конфигурации
и найдите ключGoogleService-Info.plist REVERSED_CLIENT_ID. Скопируйте значение этого ключа и вставьте его в поле URL Schemes (Схемы URL) на странице конфигурации. Другие поля оставьте без изменений.После завершения настройки конфигурация должна выглядеть примерно так (но со значениями, относящимися к вашему приложению):

Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withGoogleSignIn()
Чтобы использовать вход через Facebook:
Настройте Facebook Login для iOS SDK, следуя инструкциям на сайте Meta for Developers. Пропустите последний шаг "Добавьте вход через Facebook в свой код".
В консоли Firebase в разделе Security (Безопасность) > Authentication (Аутентификация) > Sign-in method (Способ входа) включите поставщика Facebook. Вам понадобятся идентификатор и секретный ключ приложения Facebook, которые можно найти на сайте Meta for Developers.
Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withFacebookSignIn()
Номер телефона
Чтобы использовать аутентификацию по телефону:
В разделе Security (Безопасность) > Authentication (Аутентификация) > Sign-in method (Способ входа) консоли Firebase включите поставщика Phone (Телефон).
Настройте APN для приложения, как описано в разделе Начните получать тихие уведомления.
Добавьте специальные схемы URL в проект Xcode:
Откройте конфигурацию проекта, нажав на его название в дереве аккаунта слева. Выберите приложение в разделе TARGETS (ЦЕЛИ), откройте вкладку Info (Информация) и разверните раздел URL Types (Типы URL).
Нажмите кнопку + и добавьте закодированный идентификатор приложения в качестве схемы URL. Чтобы найти это значение, откройте консоль Firebase и выберите Settings (Настройки) > General (Общие).
После завершения настройки конфигурация должна выглядеть примерно так (но со значениями, относящимися к вашему приложению):

В том же файле
AppDelegate, в котором вы вызываетеFirebaseApp.configure(), добавьте обработчики токенов APNs:import FacebookCore import FirebaseAuth import UIKit class AppDelegate: NSObject, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { FirebaseApp.configure() return true } func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { #if DEBUG Auth.auth().setAPNSToken(deviceToken, type: .sandbox) #else Auth.auth().setAPNSToken(deviceToken, type: .prod) #endif } }Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withPhoneSignIn()
X (Твиттер)
Чтобы использовать вход через Твиттер:
Создайте учетные данные X API, следуя инструкциям на сайте X Developers.
В разделе Security > Authentication > Sign-in method (Безопасность > Аутентификация > Способ входа) консоли Firebase включите поставщика Twitter. Вам понадобятся ключ API и секретный ключ API из консоли X Developer.
Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withTwitterSignIn()
Стандартные поставщики OAuth2 и OIDC
FirebaseUI также поддерживает встроенных поставщиков OAuth, таких как GitHub, Microsoft и Yahoo, а также пользовательских поставщиков OIDC, настроенных в Firebase Authentication.
let authService = AuthService()
.withOAuthSignIn(OAuthProviderSwift.github())
.withOAuthSignIn(OAuthProviderSwift.microsoft())
.withOAuthSignIn(OAuthProviderSwift.yahoo())
Если вы используете стороннего поставщика OIDC, сначала настройте его в Firebase Authentication, а затем создайте элемент OAuthProviderSwift с идентификатором поставщика и конфигурацией кнопки:
let lineProvider = OAuthProviderSwift(
providerId: "oidc.line",
buttonLabel: "Sign in with LINE",
displayName: "LINE",
iconSystemName: "person.crop.circle.badge.checkmark",
buttonBackgroundColor: .green,
buttonForegroundColor: .white
)
let authService = AuthService()
.withOAuthSignIn(lineProvider)
Как использовать встроенное представление аутентификации
FirebaseUI для SwiftUI предоставляет AuthPickerView – готовый интерфейс аутентификации, который берет на себя весь процесс аутентификации. Это самый простой способ добавить аутентификацию в приложение.
Пример
Ниже приведен пример использования AuthPickerView с несколькими поставщиками и параметрами конфигурации.
import FirebaseAppleSwiftUI
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI
struct ContentView: View {
let authService: AuthService
init() {
// Create configuration with options
let configuration = AuthConfiguration(
tosUrl: URL(string: "https://example.com/tos"),
privacyPolicyUrl: URL(string: "https://example.com/privacy"),
shouldAutoUpgradeAnonymousUsers: true
)
// Initialize AuthService with multiple providers
authService = AuthService(configuration: configuration)
.withEmailSignIn()
.withAppleSignIn()
.withGoogleSignIn()
}
var body: some View {
AuthPickerView {
authenticatedContent
}
.environment(authService)
}
var authenticatedContent: some View {
NavigationStack {
VStack(spacing: 20) {
if authService.authenticationState == .authenticated {
Text("Authenticated")
Button("Manage Account") {
authService.isPresented = true
}
.buttonStyle(.bordered)
Button("Sign Out") {
Task {
try? await authService.signOut()
}
}
.buttonStyle(.borderedProminent)
} else {
Text("Not Authenticated")
Button("Sign In") {
authService.isPresented = true
}
.buttonStyle(.borderedProminent)
}
}
.navigationTitle("My App")
}
.onChange(of: authService.authenticationState) { _, newValue in
// Automatically show auth UI when not authenticated
if newValue != .authenticating {
authService.isPresented = (newValue == .unauthenticated)
}
}
}
}
Типичная настройка
Хотя все параметры AuthConfiguration необязательны, в большинстве приложений настраиваются как минимум следующие:
let configuration = AuthConfiguration(
logo: ImageResource.exampleLogoAsset,
customStringsBundle: .main,
tosUrl: URL(string: "https://example.com/tos"),
privacyPolicyUrl: URL(string: "https://example.com/privacy"),
)
logo– логотип, который показывается на странице аутентификации. Подробнее о том, как добавить собственный объект изображения…customStringsBundle– использовать специальные строки из указанного пакета. Это используется для локализации, а также для настройки строк по умолчанию, используемыхAuthPickerView. Например, чтобы задать сообщение, которое будет показываться в верхней части окна аутентификации, создайте файлLocalizable.stringsсо следующим содержимым:"Sign in with Firebase" = "Sign in to use ExampleApp";
Что входит в стандартное представление
При использовании AuthPickerView вы получаете:
- Sheet Presentation – интерфейс аутентификации появляется в виде модального окна.
- Встроенная навигация. Автоматическая навигация между экранами входа, восстановления пароля, многофакторной аутентификации, ссылки в электронном письме и подтверждения по телефону.
- Управление состоянием аутентификации. Автоматическое переключение между интерфейсом аутентификации и контентом на основе
authService.authenticationState. - Управление с помощью
isPresented. Вы можете управлять показом окна аутентификации, задав значениеauthService.isPresented = true/false.
Поведение, основанное на убеждениях
В стандартной версии AuthPickerView есть определенные правила для обработки сложных сценариев:
1. Как устранить конфликт аккаунтов
Если возникает конфликт аккаунтов (например, при входе с учетными данными, которые уже связаны с другим аккаунтом), AuthPickerView автоматически обрабатывает его:
- Конфликты при анонимном обновлении. Если включен параметр
shouldAutoUpgradeAnonymousUsersи во время анонимного обновления возникает конфликт, система автоматически выполняет выход из анонимного аккаунта и вход с новыми учетными данными. - Другие конфликты. Если конфликт учетных данных возникает между неанонимными аккаунтами, система сохраняет ожидающие учетные данные и пытается связать их после успешного входа.
Это обрабатывается с помощью AccountConflictModifier, применяемого на уровне NavigationStack.
2. Многофакторная аутентификация (МФА)
Если в вашей конфигурации включена многофакторная аутентификация:
- Автоматически определяет, когда при входе требуется многофакторная аутентификация.
- Показывает подходящие экраны для многофакторной аутентификации (SMS или TOTP).
- Управляет регистрацией и процессами многофакторной аутентификации.
- Поддерживает факторы на основе SMS и одноразовых кодов (TOTP).
3. Обработка ошибок
В представлениях по умолчанию есть встроенная обработка ошибок:
- Показывает удобные сообщения об ошибках в диалоговых окнах.
- Автоматически отфильтровывает ошибки, которые обрабатываются внутри системы (например, ошибки отмены, автоматически обрабатываемые конфликты).
- Использует локализованные сообщения об ошибках через
StringUtils. - Ошибки распространяются через ключ среды
reportError.
4. Вход по ссылке в электронном письме
Если настроен вход по ссылке в электронном письме:
- Автоматически сохраняет адрес электронной почты в хранилище приложения.
- Обрабатывает переходы по ссылкам на контент из электронных писем.
- Управляет всем процессом подтверждения адреса электронной почты.
- Поддерживает обновление анонимных пользователей по ссылке в электронном письме.
5. Автоматическое преобразование анонимных пользователей
Если параметр shouldAutoUpgradeAnonymousUsers включен:
- Автоматически пытается связать анонимные аккаунты с новыми учетными данными для входа.
- Сохраняет пользовательские данные, обновляя анонимные сеансы, а не заменяя их.
- Корректно обрабатывает конфликты при обновлении.
6. Повторная аутентификация в представлениях по умолчанию
Для выполнения действий, связанных с безопасностью, например удаления аккаунтов, обновления паролей или отмены регистрации факторов МФА, требуется недавняя аутентификация. При использовании представлений по умолчанию повторная аутентификация выполняется автоматически в зависимости от поставщика входа пользователя.
Если для выполнения действия, связанного с безопасностью, требуется повторная аутентификация, представления по умолчанию автоматически:
Поставщики OAuth (Google, Apple, Facebook, Twitter и т. д.): Показать пользователю предупреждение с просьбой подтвердить действие, а затем автоматически получить новые учетные данные и завершить операцию.
Электронная почта/пароль. Показывает пользователю окно с предложением ввести пароль, прежде чем продолжить.
Ссылка в электронном письме. Показывает оповещение с предложением отправить письмо с подтверждением, а затем – лист с инструкциями по проверке электронной почты. Пользователь нажимает на ссылку в письме, чтобы завершить повторную аутентификацию.
Телефон. Показывает оповещение о необходимости подтверждения, а затем – окно для ввода кода из SMS.
После успешной повторной аутентификации операция будет автоматически повторена. При использовании AuthPickerView или встроенных представлений управления аккаунтом (UpdatePasswordView, SignedInView и т. д.) дополнительный код не требуется.
Дополнительно: создание собственных представлений аутентификации
Если вам нужно больше контроля над интерфейсом или навигацией, вы можете создать собственные представления аутентификации, используя AuthService для логики аутентификации.
Вы можете по-разному комбинировать элементы FirebaseUI с собственной логикой. В следующих разделах приведены примеры некоторых подходов к настройке, которые вы можете использовать.
Подход 1. Специальные кнопки с registerProvider()
Чтобы полностью контролировать внешний вид кнопки, вы можете создать собственную реализацию AuthProviderUI, которая будет возвращать кнопку в нужном вам виде.
Создание пользовательского интерфейса поставщика
Вот как создать специальную кнопку Twitter:
import FirebaseAuthSwiftUI
import FirebaseTwitterSwiftUI
import SwiftUI
// Step 1: Create your custom button view
struct CustomTwitterButton: View {
let provider: TwitterProviderSwift
@Environment(AuthService.self) private var authService
@Environment(\.mfaHandler) private var mfaHandler
var body: some View {
Button {
Task {
do {
let outcome = try await authService.signIn(provider)
// Handle MFA if required
if case let .mfaRequired(mfaInfo) = outcome,
let onMFA = mfaHandler {
onMFA(mfaInfo)
}
} catch {
// Do Something Else
}
}
} label: {
HStack { // Your custom icon
Text("Sign in with Twitter")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
.padding()
.background(
LinearGradient(
colors: [Color.blue, Color.cyan],
startPoint: .leading,
endPoint: .trailing
)
)
.foregroundColor(.white)
.cornerRadius(12)
.shadow(radius: 4)
}
}
}
// Step 2: Create a custom AuthProviderUI wrapper
class CustomTwitterProviderAuthUI: AuthProviderUI {
private let typedProvider: TwitterProviderSwift
var provider: AuthProviderSwift { typedProvider }
let id: String = "twitter.com"
init(provider: TwitterProviderSwift = TwitterProviderSwift()) {
typedProvider = provider
}
@MainActor func authButton() -> AnyView {
AnyView(CustomTwitterButton(provider: typedProvider))
}
}
// Step 3: Use it in your app
struct ContentView: View {
let authService: AuthService
init() {
let configuration = AuthConfiguration()
authService = AuthService(configuration: configuration)
// Register your custom provider UI
authService.registerProvider(
providerWithButton: CustomTwitterProviderAuthUI()
)
authService.isPresented = true
}
var body: some View {
AuthPickerView {
usersApp
}
.environment(authService)
}
var usersApp: some View {
NavigationStack {
VStack {
Button {
authService.isPresented = true
} label: {
Text("Authenticate")
}
}
}
}
}
Пример упрощенной кнопки
Вы также можете создать более простые кнопки для любого поставщика:
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI
// Custom Google Provider UI
class CustomGoogleProviderAuthUI: AuthProviderUI {
private let typedProvider: GoogleProviderSwift
var provider: AuthProviderSwift { typedProvider }
let id: String = "google.com"
init() {
typedProvider = GoogleProviderSwift()
}
@MainActor func authButton() -> AnyView {
AnyView(CustomGoogleButton(provider: typedProvider))
}
}
struct CustomGoogleButton: View {
let provider: GoogleProviderSwift
@Environment(AuthService.self) private var authService
var body: some View {
Button {
Task {
try? await authService.signIn(provider)
}
} label: {
HStack {
Image(systemName: "g.circle.fill")
Text("My Custom Google Button")
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.purple) // Your custom color
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
// Then use it
struct ContentView: View {
let authService: AuthService
init() {
let configuration = AuthConfiguration()
authService = AuthService(configuration: configuration)
.withAppleSignIn() // Use default Apple button
// Use custom Google button
authService.registerProvider(
providerWithButton: CustomGoogleProviderAuthUI()
)
}
var body: some View {
AuthPickerView {
Text("App Content")
}
.environment(authService)
}
}
Этот подход работает для всех поставщиков: Google, Apple, Twitter, Facebook, Phone и поставщиков OAuth. Просто создайте собственное представление кнопки и оберните его в класс, соответствующий AuthProviderUI.
Подход 2. Кнопки по умолчанию с настраиваемыми представлениями
Вы можете использовать AuthService.renderButtons() и обойти AuthPickerView, чтобы отобразить кнопки аутентификации по умолчанию, а также предоставить собственный макет и навигацию:
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI
struct CustomAuthView: View {
@Environment(AuthService.self) private var authService
var body: some View {
VStack(spacing: 30) {
// Your custom logo/branding
Image("app-logo")
.resizable()
.frame(width: 150, height: 150)
Text("Welcome to My App")
.font(.largeTitle)
.fontWeight(.bold)
Text("Sign in to continue")
.font(.subheadline)
.foregroundStyle(.secondary)
// Render default auth buttons
authService.renderButtons(spacing: 12)
.padding()
}
.padding()
}
}
struct ContentView: View {
init() {
let configuration = AuthConfiguration()
authService = AuthService(configuration: configuration)
.withGoogleSignIn()
.withAppleSignIn()
}
let authService: AuthService
var body: some View {
NavigationStack {
if authService.authenticationState == .authenticated {
Text("Authenticated!")
} else {
CustomAuthView()
}
}
.environment(authService)
}
}
Подход 3. Специальные представления с настраиваемой навигацией
Чтобы полностью контролировать весь процесс, вы можете не использовать AuthPickerView и создать собственную систему навигации:
import FirebaseAuth
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI
enum CustomAuthRoute {
case signIn
case phoneVerification
case mfaResolution
}
struct ContentView: View {
private let authService: AuthService
@State private var navigationPath: [CustomAuthRoute] = []
@State private var errorMessage: String?
init() {
let configuration = AuthConfiguration()
self.authService = AuthService(configuration: configuration)
.withGoogleSignIn()
.withPhoneSignIn()
}
var body: some View {
NavigationStack(path: $navigationPath) {
Group {
if authService.authenticationState == .authenticated {
authenticatedView
} else {
customSignInView
}
}
.navigationDestination(for: CustomAuthRoute.self) { route in
switch route {
case .signIn:
customSignInView
case .phoneVerification:
customPhoneVerificationView
case .mfaResolution:
customMFAView
}
}
}
.environment(authService)
.alert("Error", isPresented: .constant(errorMessage != nil)) {
Button("OK") {
errorMessage = nil
}
} message: {
Text(errorMessage ?? "")
}
}
var customSignInView: some View {
VStack(spacing: 20) {
Text("Custom Sign In")
.font(.title)
Button("Sign in with Google") {
Task {
do {
let provider = GoogleProviderSwift(clientID: Auth.auth().app?.options.clientID ?? "")
let outcome = try await authService.signIn(provider)
// Handle MFA if required
if case .mfaRequired = outcome {
navigationPath.append(.mfaResolution)
}
} catch {
errorMessage = error.localizedDescription
}
}
}
.buttonStyle(.borderedProminent)
Button("Phone Sign In") {
navigationPath.append(.phoneVerification)
}
.buttonStyle(.bordered)
}
.padding()
}
var customPhoneVerificationView: some View {
Text("Custom Phone Verification View")
// Implement your custom phone auth UI here
}
var customMFAView: some View {
Text("Custom MFA Resolution View")
// Implement your custom MFA UI here
}
var authenticatedView: some View {
VStack(spacing: 20) {
Text("Welcome!")
Text("Email: \(authService.currentUser?.email ?? "N/A")")
Button("Sign Out") {
Task {
try? await authService.signOut()
}
}
.buttonStyle(.borderedProminent)
}
}
}
Важные примечания о специальных представлениях
При создании собственных представлений вам нужно самостоятельно обрабатывать несколько вещей, которые AuthPickerView обрабатывает автоматически:
- Конфликты аккаунтов. Реализуйте собственную стратегию разрешения конфликтов с помощью
AuthServiceError.accountConflict. - Обработка многофакторной аутентификации. Проверьте
SignInOutcomeна наличие.mfaRequiredи вручную выполните многофакторную аутентификацию. - Обновление анонимных пользователей. Обработка связывания анонимных аккаунтов, если включен параметр
shouldAutoUpgradeAnonymousUsers. - Navigation State. Управление навигацией между разными экранами аутентификации (подтверждение по телефону, восстановление пароля и т. д.).
- Состояния загрузки. Показывайте индикаторы загрузки во время асинхронных операций аутентификации, отслеживая
authService.authenticationState. - Повторная аутентификация. Обрабатывайте ошибки повторной аутентификации для конфиденциальных операций (см. раздел Повторная аутентификация в пользовательских представлениях ниже).
Повторная аутентификация в пользовательских представлениях
При создании собственных представлений обрабатывайте повторную аутентификацию, отлавливая определенные ошибки и реализуя собственный процесс. При выполнении конфиденциальных операций могут возникать четыре типа ошибок повторной аутентификации, каждая из которых содержит контекстную информацию.
Шаблоны реализации
Поставщики OAuth (Google, Apple, Facebook, Twitter и т. д.)
Перехватите ошибку и вызовите reauthenticate(context:), чтобы автоматически обработать поток OAuth:
do {
try await authService.deleteUser()
} catch let error as AuthServiceError {
if case .oauthReauthenticationRequired(let context) = error {
try await authService.reauthenticate(context: context)
try await authService.deleteUser() // Retry operation
}
}
Адрес электронной почты и пароль
Перехватите ошибку, запросите пароль, создайте учетные данные и вызовите reauthenticate(with:):
do {
try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
if case .emailReauthenticationRequired(let context) = error {
// Show your password prompt UI
let password = await promptUserForPassword()
let credential = EmailAuthProvider.credential(
withEmail: context.email,
password: password
)
try await authService.reauthenticate(with: credential)
try await authService.updatePassword(to: newPassword) // Retry
}
}
Телефон:
Отловите ошибку, подтвердите номер телефона, создайте учетные данные и вызовите reauthenticate(with:):
do {
try await authService.deleteUser()
} catch let error as AuthServiceError {
if case .phoneReauthenticationRequired(let context) = error {
// Send verification code
let verificationId = try await authService.verifyPhoneNumber(
phoneNumber: context.phoneNumber
)
// Show your SMS code input UI
let code = await promptUserForSMSCode()
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationId,
verificationCode: code
)
try await authService.reauthenticate(with: credential)
try await authService.deleteUser() // Retry
}
}
Ссылка в электронном письме
Отловите ошибку, отправьте письмо с подтверждением и обработайте входящий URL:
do {
try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
if case .emailLinkReauthenticationRequired(let context) = error {
// Send verification email
try await authService.sendEmailSignInLink(
email: context.email,
isReauth: true
)
// Show your "Check your email" UI
await showCheckEmailUI()
// When user taps the link, it opens your app with a URL
// Handle it in your URL handler:
// try await authService.handleSignInLink(url: url)
// The handleSignInLink method automatically completes reauthentication
try await authService.updatePassword(to: newPassword) // Retry
}
}
Все объекты контекста повторной аутентификации включают свойство .displayMessage для текста, который видит пользователь.
Пользовательские поставщики OAuth
Вы можете создать собственные поставщики OAuth для сервисов, которые не поддерживаются по умолчанию:
⚠️ Важно! Прежде чем использовать поставщиков OIDC (OpenID Connect), их необходимо настроить в параметрах аутентификации проекта Firebase. В консоли Firebase перейдите в раздел Authentication (Аутентификация) → Sign-in method (Способ входа) и добавьте поставщика OIDC с необходимыми учетными данными (идентификатор клиента, секретный код клиента, URL издателя). Вам также необходимо зарегистрировать URI перенаправления OAuth, предоставленный Firebase, в консоли разработчика поставщика. Подробные инструкции по настройке приведены в документации по Firebase OIDC.
import FirebaseAuthSwiftUI
import FirebaseOAuthSwiftUI
import SwiftUI
struct ContentView: View {
let authService: AuthService
init() {
let configuration = AuthConfiguration()
authService = AuthService(configuration: configuration)
.withOAuthSignIn(
OAuthProviderSwift(
providerId: "oidc.line", // LINE OIDC provider
scopes: ["profile", "openid", "email"], // LINE requires these scopes
displayName: "Sign in with LINE",
buttonIcon: Image("line-logo"),
buttonBackgroundColor: .green,
buttonForegroundColor: .white
)
)
.withOAuthSignIn(
OAuthProviderSwift(
providerId: "oidc.custom-provider",
scopes: ["profile", "openid"],
displayName: "Sign in with Custom",
buttonIcon: Image(systemName: "person.circle"),
buttonBackgroundColor: .purple,
buttonForegroundColor: .white
)
)
}
var body: some View {
AuthPickerView {
Text("App Content")
}
.environment(authService)
}
}