
FirebaseUI для SwiftUI – это современная библиотека, созданная на основе Firebase Authentication и предназначенная для SwiftUI. Она предоставляет готовые потоки входа в приложение.
FirebaseUI для SwiftUI имеет следующие преимущества:
- Пользовательский интерфейс по умолчанию: добавьте полный процесс входа с помощью
AuthPickerView. - Настраиваемый. Вы можете разместить кнопки по умолчанию в собственном макете или создать полностью уникальный интерфейс.
- Связывание анонимных аккаунтов. При необходимости можно преобразовать анонимных пользователей, а не заменять их.
- Управление аккаунтом. Встроенные процессы регистрации, восстановления пароля и управления аккаунтом.
- Несколько поставщиков: электронная почта/пароль, ссылка в электронном письме, аутентификация по телефону, Apple, Google, Facebook, Twitter и стандартные поставщики OAuth2/OIDC.
- Современные функции аутентификации: встроенная поддержка многофакторной аутентификации (МФА) и асинхронных API.
Подготовка
Добавьте 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 (До следующей основной версии) и задайте минимальную версию последнего выпуска.
Нажмите Добавить пакет и выберите библиотеки, которые нужно добавить в проект.
Следующая библиотека требуется всегда. Он включает основные зависимости, а также поддержку входа с помощью адреса электронной почты и пароля и входа по ссылке из письма.
FirebaseAuthSwiftUI
Если вы хотите поддерживать другие способы входа, выберите одну или несколько из следующих библиотек:
FirebaseAppleSwiftUI(Войти через аккаунт Apple)FirebaseGoogleSwiftUI(Войти с аккаунтом Google)FirebaseFacebookSwiftUI(Войти с аккаунтом Facebook)FirebasePhoneAuthSwiftUI(аутентификация по телефону)FirebaseTwitterSwiftUI(Войти через аккаунт Twitter)FirebaseOAuthSwiftUI(стандартные поставщики OAuth и OIDC, такие как GitHub, Microsoft, Yahoo)
Нажмите Добавить пакет, чтобы установить выбранные библиотеки.
Настройте
AuthServiceв инициализаторе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) } }
Как настроить способы входа
Для каждого поддерживаемого способа входа требуется выполнить дополнительные настройки. Разверните разделы ниже и следуйте инструкциям, чтобы настроить отдельных поставщиков.
Адрес электронной почты и пароль
В разделе Безопасность > Аутентификация > Способ входа консоли Firebase включите поставщика Электронная почта/пароль.
Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withEmailSignIn()
Аутентификация по ссылке в электронном письме
Чтобы войти в аккаунт без пароля, используя ссылку в письме:
В разделе Безопасность > Аутентификация > Способ входа консоли Firebase включите Электронная почта/пароль, а затем включите вход по ссылке из письма. Обратите внимание, что для входа по ссылке из электронного письма необходимо включить вход по адресу электронной почты или паролю.
Добавьте домен ссылки в раздел Авторизованные домены.
Настройте
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":
- Включите функцию "Войти с Apple ID" для своего приложения на странице Certificates, Identifiers & Profiles (Сертификаты, идентификаторы и профили) на сайте для разработчиков Apple.
- Свяжите сайт с приложением, как описано в первом разделе статьи Настройка входа с Apple ID для сайта. Когда появится запрос, зарегистрируйте следующий 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.
В разделе Безопасность > Аутентификация > Способ входа консоли Firebase включите Apple.
- Укажите идентификатор сервиса, созданный в предыдущем разделе.
- В разделе Настройка потока кода OAuth укажите идентификатор команды Apple, закрытый ключ и идентификатор ключа, созданные в предыдущем разделе.
В Xcode откройте раздел Signing & Capabilities (Подписание и возможности) в редакторе проекта и добавьте возможность Sign in with Apple (Войти с Apple).
Зарегистрируйте поставщика в экземпляре
AuthService:let authService = AuthService() .withAppleSignIn()
Чтобы использовать вход с аккаунтом Google:
В разделе Безопасность > Аутентификация > Способ входа консоли Firebase включите поставщика Google.
Скачайте новую копию файла
GoogleService-Info.plistпроекта и скопируйте ее в проект Xcode. Перезаписать существующие версии новой.Добавьте в проект Xcode специальные схемы URL:
Откройте конфигурацию проекта: нажмите на название проекта в древовидном представлении слева. Выберите приложение в разделе ЦЕЛЕВОЙ КОНТЕНТ, откройте вкладку Информация и разверните раздел Типы URL.
Нажмите кнопку + и добавьте схему URL для обратного идентификатора клиента. Чтобы найти это значение, откройте файл конфигурации
и найдите ключGoogleService-Info.plist REVERSED_CLIENT_ID. Скопируйте значение этого ключа и вставьте его в поле Схемы URL на странице конфигурации. Оставьте остальные поля без изменений.После этого конфигурация должна выглядеть примерно так (но с вашими значениями):

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

В том же методе
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 (Твиттер)
Чтобы использовать вход через Twitter:
Создайте учетные данные X API, следуя инструкциям на сайте для разработчиков X.
В разделе Безопасность > Аутентификация > Способ входа консоли Firebase включите поставщика Twitter. Вам понадобятся ключ API и секретный код API из консоли разработчика X.
Зарегистрируйте поставщика в экземпляре
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– изображение логотипа, которое показывается на странице аутентификации. Информацию о том, как добавить собственный объект изображения, можно найти в разделе Добавление изображений в проект Xcode.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. - Состояние навигации. Управление навигацией между разными экранами аутентификации (подтверждение по телефону, восстановление пароля и т. д.).
- Состояния загрузки. Показывайте индикаторы загрузки во время асинхронных операций аутентификации, отслеживая
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)
}
}