Aggiungere l'accesso alla tua app per iOS con FirebaseUI

FirebaseUI per SwiftUI è una libreria moderna, basata su SwiftUI e creata su Firebase Authentication che fornisce flussi di accesso predefiniti alla tua app.

FirebaseUI per SwiftUI offre i seguenti vantaggi:

  • UI predefinita basata su opinioni: aggiungi un flusso di accesso completo con AuthPickerView.
  • Personalizzabile: esegui il rendering dei pulsanti predefiniti nel tuo layout o crea un'esperienza completamente personalizzata.
  • Collegamento anonimo degli account: esegui l'upgrade facoltativo degli utenti anonimi anziché sostituirli.
  • Gestione dell'account: flussi integrati per la registrazione, il recupero della password e la gestione dell'account.
  • Più provider: email/password, link via email, autenticazione telefonica, Apple, Google, Facebook, Twitter e provider OAuth2/OIDC standard.
  • Funzionalità di autenticazione moderna: supporto integrato per l'autenticazione a più fattori (MFA) e API async/await.

Prima di iniziare

Installazione

FirebaseUI per SwiftUI viene fornito come pacchetto Swift. Per installare il pacchetto nel tuo progetto Xcode:

  1. In Xcode, fai clic su File > Add Package Dependencies (File > Aggiungi dipendenze pacchetto).

  2. Inserisci l'URL del pacchetto:

    https://github.com/firebase/FirebaseUI-iOS
    
  3. Nel menu Regola di dipendenza, seleziona Fino alla prossima versione principale e imposta la versione minima su l'ultima release.

  4. Fai clic su Aggiungi pacchetto, quindi seleziona le librerie da aggiungere al progetto.

    La seguente libreria è sempre obbligatoria. Include le dipendenze principali e il supporto per l'accesso con email e password e l'accesso con link via email.

    • FirebaseAuthSwiftUI

     Se vuoi supportare altri metodi di accesso, seleziona anche una o più delle seguenti librerie:

    • FirebaseAppleSwiftUI (Accedi con Apple)
    • FirebaseGoogleSwiftUI (Accedi con Google)
    • FirebaseFacebookSwiftUI (Accedi con Facebook)
    • FirebasePhoneAuthSwiftUI (autenticazione telefonica)
    • FirebaseTwitterSwiftUI (Accedi con Twitter)
    • FirebaseOAuthSwiftUI (provider OAuth e OIDC standard come GitHub, Microsoft, Yahoo)
  5. Fai clic su Aggiungi pacchetto per installare le librerie selezionate.

  6. Configura AuthService di FirebaseUI nell'inizializzatore di View di primo livello e passa il servizio alle visualizzazioni secondarie utilizzando l'ambiente.

    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)
      }
    }
    

Configurare i metodi di accesso

Ciascuno dei metodi di accesso supportati richiede alcuni passaggi di configurazione aggiuntivi. Espandi le sezioni di seguito e segui le istruzioni per configurare i singoli fornitori.

Indirizzo email e password

  1. Nella sezione Sicurezza > Autenticazione > Metodo di accesso della console Firebase, attiva il provider Email/Password.

  2. Registra il provider nell'istanza AuthService:

    let authService = AuthService()
      .withEmailSignIn()
    

Per utilizzare l'accesso tramite link via email senza password:

  1. Nella sezione Sicurezza > Autenticazione > Metodo di accesso della console Firebase, attiva Email/Password, quindi attiva l'accesso tramite link via email. Tieni presente che l'accesso con email o password deve essere abilitato per utilizzare l'accesso tramite link via email.

  2. Aggiungi il dominio del link a Domini autorizzati.

  3. Configura ActionCodeSettings, passalo a AuthConfiguration e registra il fornitore:

    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()
    
  4. Nello stesso AppDelegate in cui chiami FirebaseApp.configure(), aggiungi la logica al metodo application(_:open:options:) che restituisce true quando l'URL aperto è un link di accesso Firebase Authentication. Questa logica indica che il link è stato gestito da FirebaseUI e non deve essere elaborato da altri gestori di link.

    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
      }
    }
    
  5. Se crei visualizzazioni personalizzate, chiama authService.handleSignInLink(url:) quando il link apre l'app.

Apple

Per utilizzare Accedi con Apple:

  1. Configura Accedi con Apple:

    1. Attiva l'accesso con Apple per la tua app nella pagina Certificati, identificatori e profili del sito per sviluppatori di Apple.
    2. Associa il tuo sito web alla tua app come descritto nella prima sezione di Configurare Accedi con Apple per il web. Quando richiesto, registra il seguente URL come URL di ritorno:
      https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
      Puoi ottenere l'ID progetto Firebase nella pagina delle impostazioni della console Firebase. Al termine, annota il nuovo ID servizio, che ti servirà nella sezione successiva.
    3. Crea una chiave privata per Accedi con Apple. Nella sezione successiva avrai bisogno della nuova chiave privata e dell'ID chiave.
    4. Se utilizzi una delle funzionalità di Firebase Authentication che inviano email agli utenti, tra cui l'accesso tramite link email, la verifica dell'indirizzo email, la revoca della modifica dell'account e altre, configura il servizio di inoltro email privato di Apple e registra noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (o il dominio del modello di email personalizzato) in modo che Apple possa inoltrare le email inviate da Firebase Authentication a indirizzi email Apple anonimizzati.
  2. Nella sezione Sicurezza > Autenticazione > Metodo di accesso della console Firebase, attiva Apple.

    • Specifica l'ID servizio che hai creato nella sezione precedente.
    • Nella sezione Configurazione del flusso di codice OAuth, specifica l'ID team Apple e la chiave privata e l'ID chiave che hai creato nella sezione precedente.
  3. In Xcode, apri la sezione Signing & Capabilities (Firma e funzionalità) dell'editor di progetto e aggiungi la funzionalità Accedi con Apple.

  4. Registra il provider nell'istanza AuthService:

    let authService = AuthService()
      .withAppleSignIn()
    

Google

Per utilizzare Accesso Google:

  1. Nella sezione Sicurezza > Autenticazione > Metodo di accesso della console Firebase, attiva il provider Google.

  2. Scarica una nuova copia del file GoogleService-Info.plist del progetto e copiala nel progetto Xcode. Sovrascrivi le versioni esistenti con quella nuova.

  3. Aggiungi schemi URL personalizzati al progetto Xcode:

    1. Apri la configurazione del progetto: fai clic sul nome del progetto nella visualizzazione ad albero a sinistra. Seleziona la tua app dalla sezione TARGET, quindi seleziona la scheda Informazioni ed espandi la sezione Tipi di URL.

    2. Fai clic sul pulsante + e aggiungi uno schema URL per l'ID client invertito. Per trovare questo valore, apri il file di configurazione GoogleService-Info.plist e cerca la chiave REVERSED_CLIENT_ID. Copia il valore di questa chiave e incollalo nella casella Schemi URL nella pagina di configurazione. Lascia invariati gli altri campi.

      Al termine, la configurazione dovrebbe essere simile alla seguente (ma con i valori specifici dell'applicazione):

  4. Registra il provider nell'istanza AuthService:

    let authService = AuthService()
      .withGoogleSignIn()
    

Facebook

Per utilizzare l'accesso con Facebook:

  1. Configura l'SDK Facebook Login per iOS seguendo le istruzioni sul sito Meta for Developers. Salta il passaggio finale "Aggiungi l'accesso con Facebook al tuo codice".

  2. Nella sezione Sicurezza > Autenticazione > Metodo di accesso della console Firebase, attiva il provider Facebook. Avrai bisogno dell'ID app Facebook e del client secret dal sito Meta for Developers.

  3. Registra il provider nell'istanza AuthService:

    let authService = AuthService()
     .withFacebookSignIn()
    

Numero di telefono

Per utilizzare l'autenticazione telefonica:

  1. Nella sezione Sicurezza > Autenticazione > Metodo di accesso della console Firebase, attiva il provider Telefono.

  2. Configura APN per la tua app come indicato nella sezione Inizia a ricevere notifiche silenziose.

  3. Aggiungi schemi URL personalizzati al progetto Xcode:

    1. Apri la configurazione del progetto: fai clic sul nome del progetto nella visualizzazione ad albero a sinistra. Seleziona la tua app dalla sezione TARGET, quindi seleziona la scheda Informazioni ed espandi la sezione Tipi di URL.

    2. Fai clic sul pulsante + e aggiungi l'ID app codificato come schema URL. Per trovare questo valore, apri Impostazioni > Generali nella console Firebase.

      Al termine, la configurazione dovrebbe essere simile alla seguente (ma con i valori specifici dell'applicazione):

  4. Nello stesso AppDelegate in cui chiami FirebaseApp.configure(), aggiungi i gestori di token 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
      }
    }
    
  5. Registra il provider nell'istanza AuthService:

    let authService = AuthService()
      .withPhoneSignIn()
    

Twitter (X)

Per utilizzare l'accesso a Twitter:

  1. Genera le credenziali API X seguendo le istruzioni sul sito X Developers.

  2. Nella sezione Sicurezza > Autenticazione > Metodo di accesso della console Firebase, attiva il provider Twitter. Avrai bisogno della chiave API e dell'API secret della console per sviluppatori X.

  3. Registra il provider nell'istanza AuthService:

    let authService = AuthService()
      .withTwitterSignIn()
    

Provider OAuth2 e OIDC standard

FirebaseUI supporta anche i provider OAuth integrati come GitHub, Microsoft e Yahoo, nonché i provider OIDC personalizzati configurati in Firebase Authentication.

 let authService = AuthService()
  .withOAuthSignIn(OAuthProviderSwift.github())
  .withOAuthSignIn(OAuthProviderSwift.microsoft())
  .withOAuthSignIn(OAuthProviderSwift.yahoo())

Per i provider OIDC personalizzati, configura prima il provider in Firebase Authentication, poi crea un OAuthProviderSwift con l'ID provider e la configurazione del pulsante:

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)

Utilizzo della visualizzazione di autenticazione predefinita

FirebaseUI per SwiftUI fornisce AuthPickerView, un'interfaccia utente di autenticazione predefinita che gestisce l'intero flusso di autenticazione per te. Questo è il modo più semplice per aggiungere l'autenticazione alla tua app.

Esempio

Ecco un esempio di AuthPickerView in uso, con più provider e opzioni di configurazione:

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)
      }
    }
  }
}

Personalizzazione tipica

Sebbene tutti i parametri AuthConfiguration siano facoltativi, la maggior parte delle app personalizza almeno le seguenti impostazioni:

let configuration = AuthConfiguration(
    logo: ImageResource.exampleLogoAsset,
    customStringsBundle: .main,
    tosUrl: URL(string: "https://example.com/tos"),
    privacyPolicyUrl: URL(string: "https://example.com/privacy"),
)
  • logo: l'immagine del logo visualizzata nel foglio di autenticazione. Per informazioni sull'inclusione di una risorsa immagine personalizzata, consulta Aggiungere immagini al progetto Xcode.

  • customStringsBundle: utilizza le stringhe personalizzate del bundle specificato. Questo valore viene utilizzato per la localizzazione, ma anche per personalizzare le stringhe predefinite utilizzate da AuthPickerView. Ad esempio, per impostare il messaggio visualizzato nella parte superiore del foglio di autenticazione, crea Localizable.strings con i seguenti contenuti:

    "Sign in with Firebase" = "Sign in to use ExampleApp";
    

Cosa è incluso nella visualizzazione predefinita

Quando utilizzi AuthPickerView, ottieni:

  1. Presentazione del foglio: l'interfaccia utente di autenticazione viene visualizzata come un foglio modale
  2. Navigazione integrata: navigazione automatica tra le schermate di accesso, recupero password, MFA, link via email e verifica telefonica
  3. Gestione dello stato di autenticazione: passa automaticamente dall'interfaccia utente di autenticazione ai tuoi contenuti in base a authService.authenticationState
  4. Controllo tramite isPresented: controlla quando viene visualizzato il foglio di autorizzazione impostando authService.isPresented = true/false

Comportamenti "guidati"

Il AuthPickerView predefinito è orientato alla gestione di diversi scenari complessi:

1. Risoluzione dei conflitti tra account

Quando si verifica un conflitto di account (ad es. l'accesso con una credenziale già collegata a un altro account), AuthPickerView lo gestisce automaticamente:

  • Conflitti di upgrade anonimi: se shouldAutoUpgradeAnonymousUsers è abilitato e si verifica un conflitto durante l'upgrade anonimo, il sistema esegue automaticamente il logout dell'utente anonimo e l'accesso con la nuova credenziale.
  • Altri conflitti: per i conflitti delle credenziali tra account non anonimi, il sistema memorizza le credenziali in attesa e tenta di collegarle dopo l'accesso riuscito.

Questa operazione viene gestita da AccountConflictModifier applicato a livello di NavigationStack.

2. Autenticazione a più fattori (MFA)

Quando l'autenticazione a più fattori (MFA) è attivata nella configurazione:

  • Rileva automaticamente quando è richiesta l'autenticazione a più fattori durante l'accesso
  • Mostra le schermate di risoluzione MFA appropriate (SMS o TOTP)
  • Gestisce i flussi di registrazione e gestione dell'autenticazione a più fattori
  • Supporta fattori basati su SMS e password unica (TOTP) basata sul tempo
3. Gestione degli errori

Le visualizzazioni predefinite includono la gestione degli errori integrata:

  • Mostra messaggi di errore di facile comprensione nelle finestre di dialogo di avviso
  • Filtra automaticamente gli errori gestiti internamente (ad es. errori di annullamento, conflitti gestiti automaticamente)
  • Utilizza messaggi di errore localizzati tramite StringUtils
  • Gli errori vengono propagati tramite la chiave dell'ambiente reportError

Quando l'accesso tramite link via email è configurato:

  • Memorizza automaticamente l'indirizzo email nello spazio di archiviazione dell'app
  • Gestisce la navigazione tramite link diretto dall'email
  • Gestisce l'intero flusso di verifica dell'email
  • Supporta gli upgrade degli utenti anonimi tramite link email
5. Upgrade automatico dell'utente anonimo

Quando shouldAutoUpgradeAnonymousUsers è attivato:

  • Tenta automaticamente di collegare gli account anonimi con le nuove credenziali di accesso
  • Conserva i dati utente eseguendo l'upgrade anziché sostituire le sessioni anonime
  • Gestisce facilmente i conflitti di upgrade
6. Riautenticazione nelle visualizzazioni predefinite

Le operazioni sensibili, come l'eliminazione di account, l'aggiornamento delle password o l'annullamento della registrazione dei fattori MFA, richiedono un'autenticazione recente. Quando utilizzi le visualizzazioni predefinite, la riautenticazione viene gestita automaticamente in base al fornitore di accesso dell'utente.

Quando un'operazione sensibile richiede la riautenticazione, le visualizzazioni predefinite vengono automaticamente:

  • Fornitori OAuth (Google, Apple, Facebook, Twitter e così via): Mostra un avviso che chiede all'utente di confermare, quindi ottieni automaticamente nuove credenziali e completa l'operazione.

  • Email/Password: mostra un foglio che chiede all'utente di inserire la password prima di continuare.

  • Link email: mostra un avviso che chiede di inviare un'email di verifica, quindi presenta un foglio con le istruzioni per controllare l'email. L'utente tocca il link nell'email per completare la riautenticazione.

  • Telefono: mostra un avviso che spiega che è necessaria la verifica, quindi presenta un foglio per la verifica del codice SMS.

L'operazione viene ritentata automaticamente dopo la riautenticazione. Non è necessario alcun codice aggiuntivo quando utilizzi AuthPickerView o le visualizzazioni di gestione degli account integrate (UpdatePasswordView, SignedInView e così via).

Avanzato: creazione di visualizzazioni di autenticazione personalizzate

Se hai bisogno di un maggiore controllo sull'interfaccia utente o sul flusso di navigazione, puoi creare visualizzazioni di autenticazione personalizzate sfruttando comunque AuthService per la logica di autenticazione.

Puoi combinare elementi di FirebaseUI con logica personalizzata in molti modi. Le seguenti sezioni contengono esempi di alcuni degli approcci di personalizzazione che potresti utilizzare.

Approccio 1: pulsanti personalizzati con registerProvider()

Per un controllo completo sull'aspetto del pulsante, puoi creare una tua implementazione AuthProviderUI personalizzata che esegua il wrapping di qualsiasi fornitore e restituisca la visualizzazione del pulsante personalizzato.

Creazione di un'interfaccia utente del fornitore personalizzata

Ecco come creare un pulsante Twitter personalizzato come esempio:

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")
        }
      }
    }
  }
}

Esempio di pulsante personalizzato semplificato

Puoi anche creare pulsanti personalizzati più semplici per qualsiasi fornitore:

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)
  }
}

Questo approccio funziona per tutti i provider: Google, Apple, Twitter, Facebook, telefono e provider OAuth. Ti basta creare la visualizzazione del pulsante personalizzato e racchiuderla in una classe conforme a AuthProviderUI.

Approccio 2: pulsanti predefiniti con visualizzazioni personalizzate

Puoi utilizzare AuthService.renderButtons() e ignorare AuthPickerView per eseguire il rendering dei pulsanti di autenticazione predefiniti fornendo il tuo layout e la tua navigazione:

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)
  }
}

Approccio 3: visualizzazioni personalizzate con navigazione personalizzata

Per un controllo completo dell'intero flusso, puoi ignorare AuthPickerView e creare il tuo sistema di navigazione:

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)
    }
  }
}

Considerazioni importanti per le viste personalizzate

Quando crei visualizzazioni personalizzate, devi gestire autonomamente diverse operazioni che AuthPickerView gestisce automaticamente:

  1. Conflitti tra account: implementa la tua strategia di risoluzione dei conflitti utilizzando AuthServiceError.accountConflict
  2. Gestione MFA: controlla SignInOutcome per .mfaRequired e gestisci manualmente la risoluzione MFA
  3. Upgrade degli utenti anonimi: gestisci il collegamento degli account anonimi se shouldAutoUpgradeAnonymousUsers è abilitato
  4. Stato di navigazione: gestisci la navigazione tra le diverse schermate di autenticazione (verifica del telefono, recupero della password e così via).
  5. Stati di caricamento: mostra gli indicatori di caricamento durante le operazioni di autenticazione asincrona osservando authService.authenticationState
  6. Riautenticazione: gestisci gli errori di riautenticazione per le operazioni sensibili (vedi Riautenticazione nelle visualizzazioni personalizzate di seguito)

Riautenticazione nelle visualizzazioni personalizzate

Quando crei visualizzazioni personalizzate, gestisci la riautenticazione rilevando errori specifici e implementando il tuo flusso. Le operazioni sensibili generano quattro tipi di errori di riautenticazione, ognuno dei quali contiene informazioni di contesto.

Pattern di implementazione

Fornitori OAuth (Google, Apple, Facebook, Twitter e così via):

Rileva l'errore e chiama reauthenticate(context:), che gestisce automaticamente il flusso 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
  }
}

Email/Password:

Rileva l'errore, richiedi la password, crea le credenziali e chiama 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
  }
}

Telefono:

Rileva l'errore, verifica il telefono, crea le credenziali e chiama 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
  }
}

Link email:

Rileva l'errore, invia l'email di verifica e gestisci l'URL in entrata:

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
  }
}

Tutti gli oggetti di contesto di riautenticazione includono una proprietà .displayMessage per il testo rivolto agli utenti.

Provider OAuth personalizzati

Puoi creare provider OAuth personalizzati per servizi diversi da quelli integrati:

⚠️ Importante:i provider OIDC (OpenID Connect) devono essere configurati nelle impostazioni di autenticazione del progetto Firebase prima di poter essere utilizzati. Nella console Firebase, vai a Autenticazione → Metodo di accesso e aggiungi il tuo provider OIDC con le credenziali richieste (ID client, segreto client, URL emittente). Devi anche registrare l'URI di reindirizzamento OAuth fornito da Firebase nella console per sviluppatori del tuo provider. Per istruzioni di configurazione dettagliate, consulta la documentazione di 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)
  }
}