Creare con Firebase Data Connect (iOS / Swift)

1. Panoramica

Questo codelab ti guida nel processo di integrazione di Firebase Data Connect con un database Cloud SQL per creare un'app di recensioni di film per iOS utilizzando SwiftUI

Scoprirai come connettere la tua applicazione iOS a un database Cloud SQL utilizzando Firebase Data Connect, consentendo la sincronizzazione perfetta dei dati per le recensioni di film.

Al termine di questo codelab, avrai un'app per iOS funzionante che consente agli utenti di sfogliare i film e contrassegnarli come preferiti, il tutto supportato da un database Cloud SQL che sfrutta la potenza di Firebase Data Connect.

Obiettivi didattici

Questo codelab ti insegnerà a:

  • Configura Firebase Data Connect utilizzando la suite di emulatori Firebase per tempi di risposta rapidi.
  • Progetta uno schema di database utilizzando Data Connect e GraphQL.
  • Crea un SDK Swift typesafe dallo schema del database e aggiungilo a un'applicazione Swift.
  • Implementa l'autenticazione utente e integrala con Firebase Data Connect per proteggere i dati degli utenti.
  • Recupera, aggiorna, elimina e gestisci i dati in Cloud SQL utilizzando query e mutazioni basate su GraphQL.
  • (Facoltativo) Esegui il deployment di un servizio Data Connect in produzione.

Prerequisiti

  • L'ultima versione di Xcode
  • Il codice campione del codelab. Scaricherai il codice di esempio in uno dei primi passaggi del codelab.

2. Configurare il progetto di esempio

Crea un progetto Firebase

  1. Accedi alla console Firebase utilizzando il tuo Account Google.
  2. Fai clic sul pulsante per creare un nuovo progetto, quindi inserisci un nome per il progetto (ad esempio Friendly Flix).
  3. Fai clic su Continua.
  4. Se richiesto, leggi e accetta i termini di Firebase, quindi fai clic su Continua.
  5. (Facoltativo) Attiva l'assistenza AI nella console Firebase (denominata "Gemini in Firebase").
  6. Per questo codelab non hai bisogno di Google Analytics, quindi disattiva l'opzione Google Analytics.
  7. Fai clic su Crea progetto, attendi il provisioning del progetto, poi fai clic su Continua.

Scarica il codice

Esegui questo comando per clonare il codice di esempio per questo codelab. Verrà creata una directory denominata codelab-dataconnect-ios sulla tua macchina:

git clone https://github.com/FirebaseExtended/codelab-dataconnect-ios`

Se non hai git sul tuo computer, puoi anche scaricare il codice direttamente da GitHub.

Aggiungi la configurazione di Firebase

L'SDK Firebase utilizza un file di configurazione per connettersi al tuo progetto Firebase. Sulle piattaforme Apple, questo file si chiama GoogleServices-Info.plist. In questo passaggio, scaricherai il file di configurazione e lo aggiungerai al progetto Xcode.

  1. Nella console Firebase, seleziona Panoramica del progetto nel menu di navigazione a sinistra.
  2. Fai clic sul pulsante iOS+ per selezionare la piattaforma. Quando ti viene richiesto l'ID pacchetto Apple, utilizza com.google.firebase.samples.FriendlyFlix
  3. Fai clic su Registra app e segui le istruzioni per scaricare il file GoogleServices-Info.plist.
  4. Sposta il file scaricato nella directory start/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/ del codice che hai appena scaricato, sostituendo il file GoogleServices-Info.plist esistente.
  5. Poi fai clic su Avanti un paio di volte per completare il progetto di configurazione nella console Firebase (non devi aggiungere l'SDK all'app, perché è già stato fatto nel progetto iniziale).
  6. Infine, fai clic su Continua alla console per completare la procedura di configurazione.

3. Configura Data Connect

Installazione

Installazione automatica

Esegui questo comando nella directory codelab-dataconnect-ios/FriendlyFlix:

curl -sL https://firebase.tools/dataconnect | bash

Questo script tenta di configurare l'ambiente di sviluppo per te e avvia un IDE basato su browser. Questo IDE fornisce strumenti, tra cui un'estensione VS Code pre-inclusa, per aiutarti a gestire lo schema e definire query e mutazioni da utilizzare nella tua applicazione, nonché a generare SDK fortemente tipizzati.

Dopo l'esecuzione dello script, VS Code dovrebbe aprirsi automaticamente.

Dopo averlo fatto una volta, puoi avviare VS Code eseguendolo nella directory locale:

code .

Installazione manuale

  1. Installa Visual Studio Code
  2. Installa Node.js
  3. In VS Code, apri la directory codelab-dataconnect-ios/FriendlyFlix.
  4. Installa l'estensione Firebase Data Connect da Visual Studio Code Marketplace.

Inizializza Data Connect nel progetto

Nel riquadro a sinistra, fai clic sull'icona di Firebase per aprire la UI dell'estensione Data Connect VS Code

  1. Fai clic sul pulsante Accedi con Google. Si aprirà una finestra del browser. Segui le istruzioni per accedere all'estensione con il tuo Account Google.
  2. Fai clic sul pulsante Collega un progetto Firebase e seleziona il progetto che hai creato in precedenza nella console.
  3. Fai clic sul pulsante Esegui firebase init e segui i passaggi nel terminale integrato.

Configura la generazione dell'SDK

Dopo aver fatto clic sul pulsante Esegui firebase init, l'estensione Firebase Data Connect dovrebbe inizializzare una directory dataconnect.

In VS Code, apri il file dataconnect/connector/connector.yaml e troverai la configurazione predefinita.

Aggiorna la configurazione e utilizza le seguenti impostazioni per assicurarti che il codice generato funzioni con questo codelab. In particolare, assicurati che connectorId sia impostato su friendly-flix e il pacchetto Swift su FriendlyFlixSDK.

connectorId: "friendly-flix"
generate:
  swiftSdk:
    outputDir: "../../app"
    package: "FriendlyFlixSDK"
    observablePublisher: observableMacro

Ecco il significato di queste impostazioni:

  • connectorId: un nome univoco per questo connettore.
  • outputDir: percorso in cui verrà archiviato l'SDK Data Connect generato. Questo percorso è relativo alla directory contenente il file connector.yaml.
  • package: il nome del pacchetto da utilizzare per il pacchetto Swift generato.

Dopo aver salvato questo file, Firebase Data Connect genererà un pacchetto Swift denominato FriendlyFlixSDK e lo inserirà accanto alla cartella del progetto FriendlyFlix.

Avvia gli emulatori Firebase

In VS Code, passa alla visualizzazione Firebase e poi fai clic sul pulsante Avvia emulatori.

Verrà avviato l'emulatore Firebase nel terminale integrato. L'output dovrebbe essere simile al seguente:

npx -y firebase-tools@latest emulators:start --project <your-project-id>

Aggiungi il pacchetto generato alla tua app Swift

  1. Apri FriendlyFlix/app/FriendlyFlix/FriendlyFlix.xcodeproj in Xcode
  2. Seleziona File > Add Package Dependencies… (File > Aggiungi dipendenze pacchetto…)
  3. Fai clic su Aggiungi locale…, poi aggiungi il pacchetto FriendlyFlixSDK dalla cartella FriendlyFlix/app.
  4. Attendi che Xcode risolva le dipendenze del pacchetto.
  5. Nella finestra di dialogo Scegli i prodotti del pacchetto per FriendlyFlixSDK, seleziona FriendlyFlix come target e fai clic su Aggiungi pacchetto.

Configura l'app iOS in modo che utilizzi l'emulatore locale

  1. Apri FriendlyFlixApp.swift. Puoi premere CMD + Maiusc + O per aprire la finestra di dialogo Apertura rapida, quindi digitare "FriendlyFlixApp" per trovare rapidamente il file.
  2. Importa Firebase, Firebase Auth, Firebase Data Connect e l'SDK generato per lo schema
  3. Nel programma di inizializzazione, configura Firebase.
  4. Assicurati che Data Connect e Firebase Auth utilizzino l'emulatore locale.
import SwiftUI
import os
import Firebase
import FirebaseAuth
import FriendlyFlixSDK
import FirebaseDataConnect

@main
struct FriendlyFlixApp: App {
  ...

  init() {
    FirebaseApp.configure()
    if useEmulator {
      DataConnect.friendlyFlixConnector.useEmulator(port: 9399)
      Auth.auth().useEmulator(withHost: "localhost", port: 9099)
    }

    authenticationService = AuthenticationService()
  }

  ...

}
  1. Seleziona un simulatore iOS nel menu a discesa Destinazione.
  2. Premi CMD+R (o fai clic sul pulsante Esegui) in Xcode per eseguire l'app su un simulatore.

4. Definisci lo schema e precompila il database

In questa sezione definirai la struttura e le relazioni tra le entità chiave nell'applicazione per film in uno schema. Entità come Movie, MovieMetaData e altre vengono mappate alle tabelle del database, con relazioni stabilite utilizzando Firebase Data Connect e le direttive dello schema GraphQL.

Entità e relazioni principali

Il modello di dati per questa app di monitoraggio dei film è costituito da diverse entità che creerai nel corso di questo codelab. Creerai prima le entità principali e, man mano che implementi sempre più funzionalità, aggiungerai le entità richieste per queste funzionalità.

In questo passaggio creerai i tipi Movie e MovieMetadata.

Film

Il tipo Movie definisce la struttura principale di un'entità film, inclusi campi come title, genre, releaseYear e rating.

In VS Code, aggiungi la definizione del tipo Movie a dataconnect/schema/schema.gql:

type Movie @table {
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  imageUrl: String!
  releaseYear: Int
  genre: String
  rating: Float
  description: String
  tags: [String]
}

MovieMetadata

Il tipo MovieMetadata stabilisce una relazione one-to-one con il tipo Movie. Include dati aggiuntivi, come il regista del film.

Aggiungi la definizione della tabella MovieMetadata al file dataconnect/schema/schema.gql:

type MovieMetadata @table {
  movie: Movie! @ref
  director: String
}

Campi e valori predefiniti generati automaticamente

Lo schema utilizza espressioni come @default(expr: "uuidV4()") per generare automaticamente ID e timestamp univoci. Ad esempio, il campo id nel tipo Movie viene compilato automaticamente con un UUID quando viene creato un nuovo record.

Inserisci dati simulati per film e metadati dei film

Una volta definito lo schema, puoi precompilare il database con dati simulati per i test.

  1. In Finder, copia finish/FriendlyFlix/dataconnect/moviedata_insert.gql nella cartella start/FriendlyFlix/dataconnect.
  2. In VS Code, apri dataconnect/moviedata_insert.gql.
  3. Assicurati che gli emulatori nell'estensione Firebase Data Connect siano in esecuzione.
  4. Dovresti vedere un pulsante Esegui (locale) nella parte superiore del file. Fai clic qui per inserire i dati simulati dei film nel database.
  5. Controlla il terminale Data Connect Execution per verificare che i dati siano stati aggiunti correttamente.

Con i dati a disposizione, procedi al passaggio successivo per scoprire come creare query in Data Connect.

5. Recuperare e visualizzare film

In questa sezione implementerai una funzionalità per visualizzare un elenco di film.

Innanzitutto, imparerai a creare una query che recupera tutti i film dalla tabella movies. Firebase Data Connect genera il codice per un SDK typesafe che puoi utilizzare per eseguire la query e visualizzare i film recuperati nell'interfaccia utente della tua app.

Definisci la query ListMovies

Le query in Firebase Data Connect sono scritte in GraphQL, il che ti consente di specificare i campi da recuperare. In FriendlyFlix, le schermate che mostrano i film richiedono i seguenti campi: title, description, releaseYear, rating e imageUrl. Inoltre, poiché si tratta di un'app SwiftUI, avrai bisogno di id per facilitare l'identità della visualizzazione SwiftUI.

In VS Code, apri il file dataconnect/connector/queries.gql e aggiungi la query ListMovies:

query ListMovies @auth(level: PUBLIC) {
  movies {
    id
    title
    imageUrl
    releaseYear
    genre
    rating
    tags
    description
  }
}

Per testare la nuova query, fai clic sul pulsante Esegui (locale) per eseguirla sul database locale. L'elenco dei film del database dovrebbe essere visualizzato nella sezione Risultati del terminale di esecuzione di Data Connect.

Collega la query ListMovies alla schermata Home dell'app

Ora che hai testato la query nell'emulatore Data Connect, puoi chiamarla dall'interno dell'app.

Quando salvi queries.gql, Firebase Data Connect genera il codice corrispondente alla query ListMovies nel pacchetto FriendlyFlixSDK.

In Xcode, apri Movie+DataConnect.swift e aggiungi il seguente codice per mappare da ListMoviesQuery.Data.Movie a Movie:

import FirebaseDataConnect
import FriendlyFlixSDK

extension Movie {
  init(from: ListMoviesQuery.Data.Movie) {
    id = from.id
    title = from.title
    description = from.description ?? ""
    releaseYear = from.releaseYear
    rating = from.rating
    imageUrl = from.imageUrl
  }
}

Apri il file HomeScreen.swift e aggiornalo utilizzando il seguente snippet di codice.

import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

struct HomeScreen: View {
  ...

  private var connector = DataConnect.friendlyFlixConnector
  let heroMoviesRef: QueryRefObservation<ListMoviesQuery.Data, ListMoviesQuery.Variables>

  init() {
    heroMoviesRef = connector.listMoviesQuery.ref()
  }
}

extension HomeScreen {
  ...

  private var heroMovies: [Movie] {
    heroMoviesRef.data?.movies.map(Movie.init) ?? []
  }

 private var topMovies: [Movie] {
    heroMoviesRef.data?.movies.map(Movie.init) ?? []
  }

  private var watchList: [Movie] {
    heroMoviesRef.data?.movies.map(Movie.init) ?? []
  }

  ...
}

La query listMoviesQuery() è stata generata da Data Connect quando hai salvato queries.gql. Per visualizzare l'implementazione Swift, controlla il file FriendlyFlixOperations.swift nel pacchetto FriendlyFlixSDK.

Esegui l'app

In Xcode, fai clic sul pulsante Esegui per avviare l'app in iOS Simulator.

Una volta avviata l'app, dovresti visualizzare una schermata simile a questa:

Potresti notare che tutte le aree dell'app (la sezione principale, i film più popolari e la lista di visualizzazione) mostrano lo stesso elenco. Questo perché utilizzi la stessa query per tutte queste viste. Nelle sezioni successive implementerai le query personalizzate.

6. Visualizzare l'immagine hero e i film più popolari

In questo passaggio, ti concentrerai sull'aggiornamento della modalità di visualizzazione dei film nella sezione in primo piano, ovvero il carosello in evidenza nella parte superiore della schermata Home, e anche nella sezione dei film più popolari sottostante.

Al momento, la query ListMovies recupera tutti i film. Per ottimizzare la visualizzazione di queste sezioni, limiterai il numero di film restituiti da ogni query. L'implementazione attuale della query ListMovies non offre ancora il supporto integrato per la limitazione dei risultati. L'aggiunta del supporto per la limitazione e l'ordinamento è un'operazione che eseguirai in questa sezione.

Migliorare la query ListMovies

Apri queries.gql e aggiorna ListMovies come segue per aggiungere il supporto per l'ordinamento e la limitazione:

query ListMovies(
  $orderByRating: OrderDirection
  $orderByReleaseYear: OrderDirection
  $limit: Int
) @auth(level: PUBLIC) {
  movies(
    orderBy: [{ rating: $orderByRating }, { releaseYear: $orderByReleaseYear }]
    limit: $limit
  ) {
    id
    title
    description
    releaseYear
    rating
    imageUrl
  }
}

In questo modo potrai limitare il numero di film restituiti dalla query e ordinare il set di risultati in base alla valutazione e all'anno di uscita.

Una volta salvato questo file, Firebase Data Connect rigenererà automaticamente il codice all'interno di FriendlyFlixSDK. Nel passaggio successivo, puoi aggiornare il codice in HomeScreen.swift per utilizzare queste funzionalità aggiuntive.

Utilizzare la query avanzata nell'UI

Torna a Xcode per apportare le modifiche necessarie a HomeScreen.swift.

Innanzitutto, aggiorna heroMoviesRef per recuperare i tre film usciti più di recente:

struct HomeScreen {
  ...

  init() {
    heroMoviesRef = connector.listMoviesQuery
      .ref { optionalVars in
        optionalVars.limit = 3
        optionalVars.orderByReleaseYear = .DESC
      }

  }
}

Successivamente, configura un altro riferimento alla query per i film più votati e imposta il filtro sui 5 film con la valutazione più alta:

struct HomeScreen {
  ...

  let topMoviesRef: QueryRefObservation<ListMoviesQuery.Data, ListMoviesQuery.Variables>

  init() {
    heroMoviesRef = ...

    topMoviesRef = connector.listMoviesQuery
      .ref { optionalVars in
        optionalVars.limit = 5
        optionalVars.orderByRating = .DESC
      }
  }
}

Infine, aggiorna la proprietà calcolata che collega il risultato di questa query alla UI:

extension HomeScreen {
  ...

  private var topMovies: [Movie] {
    topMoviesRef.data?.movies.map(Movie.init) ?? []
  }

}

Guarda come funziona

Esegui di nuovo l'app per visualizzare i 3 film più recenti nella sezione principale e i 5 film con la valutazione più alta nella sezione dei film più popolari:

7. Visualizzare i dettagli di film e attori

L'utente ora può sfogliare i film. Quando tocchi la scheda di un film, vengono visualizzati alcuni dettagli, ma potresti aver notato che mancano alcuni particolari.

Questo perché abbiamo recuperato solo i dettagli di ogni film necessari per il rendering della sezione promozionale e della sezione dei film più popolari: il titolo del film, una breve descrizione e l'URL dell'immagine.

Nella pagina dei dettagli del film, vogliamo mostrare maggiori informazioni sul film. In questa sezione, migliorerai l'app in modo che possa mostrare gli attori del film e le recensioni nella pagina dei dettagli.

A questo scopo, dovrai fare un paio di cose:

  • Migliorare lo schema per supportare attori e recensioni di film
  • Scrivi query Firebase Data Connect per recuperare i dettagli di un determinato film
  • Mostra i risultati nella schermata dei dettagli del film

Migliorare lo schema

In VS Code, apri dataconnect/schema/schema.gql e aggiungi le definizioni dello schema per Actor e MovieActor.

## Actors
## An actor can participate in multiple movies; movies can have multiple actors
## Movie - Actors (or vice versa) is a many to many relationship
type Actor @table {
  id: UUID!
  imageUrl: String!
  name: String! @col(name: "name", dataType: "varchar(30)")
}

## Join table for many-to-many relationship for movies and actors
## The 'key' param signifies the primary key(s) of this table
## In this case, the keys are [movieId, actorId], the generated fields of the reference types [movie, actor]
type MovieActor @table(key: ["movie", "actor"]) {
  ## @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
  ## In this case, @ref(fields: "id") is implied
  movie: Movie!
  ## movieId: UUID! <- this is created by the implied @ref, see: implicit.gql

  actor: Actor!
  ## actorId: UUID! <- this is created by the implied  @ref, see: implicit.gql

  role: String! ## "main" or "supporting"
}

Aggiungere dati simulati per gli attori

Con lo schema aggiornato, ora puoi popolare il database con altri dati simulati per i test.

  1. In Finder, copia finish/FriendlyFlix/dataconnect/moviededetails_insert.gql nella cartella start/FriendlyFlix/dataconnect.
  2. In VS Code, apri dataconnect/moviededetails_insert.gql.
  3. Assicurati che gli emulatori nell'estensione Firebase Data Connect siano in esecuzione.
  4. Dovresti vedere un pulsante Esegui (locale) nella parte superiore del file. Fai clic qui per inserire i dati simulati dei film nel database.
  5. Controlla il terminale di esecuzione di Data Connect per verificare che i dati siano stati aggiunti correttamente.

Con i dati inseriti, procedi al passaggio successivo per definire la query per recuperare i dettagli del film.

Definisci la query GetMovieById

In VS Code, apri il file dataconnect/connector/queries.gql e aggiungi la query GetMovieById:

## Get movie by id
query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
  movie(id: $id) {
    id
    title
    imageUrl
    releaseYear
    genre
    rating
    description
    tags
    metadata: movieMetadatas_on_movie {
      director
    }
    mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) {
      id
      name
      imageUrl
    }
    supportingActors: actors_via_MovieActor(
      where: { role: { eq: "supporting" } }
    ) {
      id
      name
      imageUrl
    }
  }
}

Connetti la query GetMovieById a MovieDetailsView

In Xcode, apri il file MovieDetailsView.swift e aggiorna la proprietà calcolata movieDetails in modo che corrisponda al seguente codice:

import NukeUI
import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

@MainActor
struct MovieDetailsView: View {
  private var movie: Movie

  private var movieDetails: MovieDetails? {
    DataConnect.friendlyFlixConnector
      .getMovieByIdQuery
      .ref(id: movie.id)
      .data?.movie.map { movieDetails in
        MovieDetails(
          title: movieDetails.title,
          description: movieDetails.description ?? "",
          releaseYear: movieDetails.releaseYear,
          rating: movieDetails.rating ?? 0,
          imageUrl: movieDetails.imageUrl,
          mainActors: movieDetails.mainActors.map { mainActor in
            MovieActor(id: mainActor.id,
                       name: mainActor.name,
                       imageUrl: mainActor.imageUrl)
          },
          supportingActors: movieDetails.supportingActors.map{ supportingActor in
            MovieActor(id: supportingActor.id,
                       name: supportingActor.name,
                       imageUrl: supportingActor.imageUrl)
          },
          reviews: []
        )
      }
  }

  public init(movie: Movie) {
    self.movie = movie
  }
}

Esegui l'app

In Xcode, fai clic sul pulsante Esegui per avviare l'app sul simulatore iOS.

Una volta avviata l'app, tocca la scheda di un film per visualizzarne i dettagli. Dovrebbe avere il seguente aspetto:

8. Implementare l'autenticazione utente

Al momento, l'app mostra informazioni non personalizzate su film e attori. Nei passaggi successivi, implementerai funzionalità che associano i dati all'utente che ha eseguito l'accesso. Inizierai consentendo agli utenti di aggiungere film alla loro lista di titoli personale.

Prima di poter implementare la funzionalità della watchlist, devi prima stabilire l'identità dell'utente. Per attivare questa funzionalità, devi integrare Firebase Authentication, che consente agli utenti di accedere all'app.

Potresti aver già notato il pulsante dell'avatar utente in alto a destra nella schermata Home. Se lo tocchi, si apre una schermata in cui gli utenti possono registrarsi o accedere utilizzando il proprio indirizzo email e la propria password.

Una volta che un utente ha eseguito l'accesso, la tua app dovrà memorizzare i suoi dati essenziali, principalmente il suo ID utente univoco e il nome utente scelto.

Attivare Firebase Authentication

Nella console Firebase del tuo progetto, vai alla sezione Authentication e attiva Firebase Authentication. Poi, attiva il provider di autenticazione tramite email/password.

Nella cartella del progetto locale, trova firebase.json e aggiornalo come segue per abilitare l'emulatore Firebase Authentication.

{
  "emulators": {
    "dataconnect": {
    },
    "auth": {
    }
  },
  "dataconnect": {
    "source": "dataconnect"
  }
}

Dopodiché, devi arrestare e riavviare l'emulatore Firebase affinché la modifica venga applicata.

Implementare un gestore autenticazione

Nella sezione seguente, implementerai la logica che collega l'autenticazione utente al tuo database. Ciò comporta la creazione di un gestore autenticazione che ascolti gli accessi riusciti.

Una volta autenticato un utente, questo gestore attiverà automaticamente la creazione del relativo account nel tuo database.

In Xcode, apri il file AuthenticationService.swift e aggiungi il seguente codice:

import Foundation
import Observation
import os
import FirebaseAuth

enum AuthenticationState {
  case unauthenticated
  case authenticating
  case authenticated
}

@Observable
class AuthenticationService {
  private let logger = Logger(subsystem: "FriendlyFlix", category: "auth")

  var presentingAuthenticationDialog = false
  var presentingAccountDialog = false

  var authenticationState: AuthenticationState = .unauthenticated
  var user: User?
  private var authenticationListener: AuthStateDidChangeListenerHandle?

  init() {
    authenticationListener = Auth.auth().addStateDidChangeListener { auth, user in
      if let user {
        self.authenticationState = .authenticated
        self.user = user
      } else {
        self.authenticationState = .unauthenticated
      }
    }
  }

  private var onSignUp: ((User) -> Void)?
  public func onSignUp(_ action: @escaping (User) -> Void) {
    onSignUp = action
  }

  func signInWithEmailPassword(email: String, password: String) async throws {
    try await Auth.auth().signIn(withEmail: email, password: password)
    authenticationState = .authenticated
  }

  func signUpWithEmailPassword(email: String, password: String) async throws {
    try await Auth.auth().createUser(withEmail: email, password: password)

    if let onSignUp, let user = Auth.auth().currentUser {
      logger
        .debug(
          "User signed in \(user.displayName ?? "(no fullname)") with email \(user.email ?? "(no email)")"
        )
      onSignUp(user)
    }

    authenticationState = .authenticated
  }

  func signOut() throws {
    try Auth.auth().signOut()
    authenticationState = .unauthenticated
  }
}

Si tratta di un gestore di autenticazione generico che consente di utilizzare onSignUp per registrare una chiusura che verrà chiamata quando l'utente avrà eseguito l'accesso.

All'interno di questa chiusura, puoi creare un nuovo account utente nel database. Prima di poterlo fare, devi creare una mutazione che ti consenta di creare o aggiornare nuovi utenti nel database.

Aggiungere un'entità Utente allo schema

Il tipo User definisce un'entità utente. Gli utenti possono interagire con i film lasciando recensioni o aggiungendoli ai preferiti.

In VS Code, apri il file dataconnect/schema/schema.gql e aggiungi la seguente definizione della tabella User:

## Users
## A user can leave reviews for movies
## user-reviews is a one to many relationship, movie-reviews is a one to many relationship, movie:user is a many to many relationship
type User @table {
  id: String! @col(name: "user_auth")
  username: String! @col(name: "username", dataType: "varchar(50)")
}

Definisci una mutazione per inserire o aggiornare un utente

In VS Code, apri il file dataconnect/connector/mutations.gql e aggiungi la mutazione UpsertUser:

mutation UpsertUser($username: String!) @auth(level: USER) {
  user_upsert(
    data: {
      id_expr: "auth.uid"
      username: $username
    }
  )
}

Creare un nuovo utente dopo l'accesso

In Xcode, apri FriendlyFlixApp.swift e aggiungi il seguente codice all'inizializzatore:

@main
struct FriendlyFlixApp: App {

  ...

  init() {
    ...
    authenticationService = AuthenticationService()
    authenticationService?.onSignUp { user in
      let userName = String(user.email?.split(separator: "@").first ?? "(unknown)")
      Task {
        try await DataConnect.friendlyFlixConnector
          .upsertUserMutation.execute(username: userName)
      }
    }
  }

  var body: some Scene {
    ...
  }
}

Questo codice utilizza upsertUserMutation Firebase Data Connect generato per inserire un nuovo utente (o aggiornare un utente esistente con lo stesso ID) ogni volta che un utente si registra correttamente utilizzando Firebase Authentication.

Guarda come funziona

Per verificare che funzioni, registrati prima nell'app per iOS:

  • In caso contrario, arresta e riavvia l'emulatore Firebase per assicurarti che l'emulatore Firebase Authentication sia in esecuzione.
  • In Xcode, fai clic sul pulsante Esegui per avviare l'app sul simulatore iOS.
  • Fai clic sull'icona dell'avatar nell'angolo in alto a destra dello schermo.
  • Passa al flusso Registrati e registrati all'app.

Quindi, esegui una query sul database per verificare che l'app abbia creato un nuovo account utente per l'utente:

  • In VS Code, apri dataconnect/schema/schema.gql e fai clic su Leggi dati nell'entità User.
  • Verrà creato un nuovo file di query denominato User_read.gql.
  • Fai clic su Esegui locale per visualizzare tutti gli utenti nella tabella degli utenti.
  • Nel riquadro Esecuzione di Data Connect, ora dovresti vedere un account per l'utente con cui hai appena eseguito la registrazione

9. Gestire i film preferiti

In questa sezione del codelab, implementerai le interazioni degli utenti nell'app di recensioni di film, in particolare consentendo agli utenti di gestire i loro film preferiti. I film contrassegnati come preferiti verranno visualizzati nella sezione Lista di titoli dell'app.

Migliorare lo schema per supportare i preferiti

Il tipo FavoriteMovie è una tabella di join che gestisce le relazioni molti-a-molti tra gli utenti e i loro film preferiti. Ogni tabella collega un User a un Movie.

Copia e incolla lo snippet di codice nel file dataconnect/schema/schema.gql:

type FavoriteMovie
  @table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
  ## @ref is implicit
  user: User!
  movie: Movie!
}

Definisci le mutazioni per l'aggiunta e la rimozione dei preferiti

Prima che l'app possa mostrare i film preferiti dell'utente, quest'ultimo deve indicare quali sono i suoi preferiti. Per farlo, devi prima aggiungere due mutazioni per contrassegnare un film come preferito dell'utente o rimuoverlo di nuovo dai preferiti.

  1. In VS Code, apri mutations.gql in dataconnect/connector/mutations.gql
  2. Aggiungi le seguenti mutazioni per gestire l'aggiunta di film ai preferiti:
## Add a movie to the user's favorites list
mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId })
}

## Remove a movie from the user's favorites list
mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}

Collega le mutazioni alla UI della tua app

Gli utenti possono contrassegnare un film come preferito facendo clic sull'icona a forma di cuore nella schermata dei dettagli del film.

Per collegare le mutazioni che hai appena creato alla UI dell'app, apporta le seguenti modifiche in MovieCardView:

  1. Importa FriendlyFlixSDK e configura il connettore
import NukeUI
import os
import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

struct MovieCardView: View {
  private let logger = Logger(subsystem: "FriendlyFlix", category: "moviecard")
  @Environment(\.dismiss) private var dismiss
  private var connector = DataConnect.friendlyFlixConnector

  ...
}
  1. Implementa il metodo toggleFavourite. Verrà chiamato ogni volta che l'utente tocca l'icona a forma di cuore in MovieCardView:
struct MovieCardView {

  ...

  private func toggleFavourite() {
    Task {
      if isFavourite {
        let _ = try await connector.deleteFavoritedMovieMutation.execute(movieId: movie.id)
      } else {
        let _ = try await connector.addFavoritedMovieMutation.execute(movieId: movie.id)
      }
    }
  }
}

Verrà aggiornato lo stato di preferito del film corrente nel database. L'ultimo passaggio mancante è assicurarsi che lo stato della UI venga visualizzato di conseguenza.

Definisci una query per scoprire se un film è contrassegnato come preferito

  1. In VS Code, apri queries.gql in dataconnect/connector.
  2. Aggiungi la seguente query per verificare se un film è contrassegnato come preferito:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
    movieId
  }
}
  1. In Xcode, crea un'istanza di un riferimento alla query GetIfFavoritedMovie e implementa la proprietà calcolata che determina se il film mostrato in questo MovieCardView è contrassegnato come preferito per l'utente corrente.
struct MovieCardView: View {

  ...

  public init(showDetails: Bool, movie: Movie) {
    self.showDetails = showDetails
    self.movie = movie

    isFavouriteRef = connector.getIfFavoritedMovieQuery.ref(movieId: movie.id)
  }

  // MARK: - Favourite handling

  private let isFavouriteRef: QueryRefObservation<
    GetIfFavoritedMovieQuery.Data,
    GetIfFavoritedMovieQuery.Variables
  >
  private var isFavourite: Bool {
    isFavouriteRef.data?.favorite_movie?.movieId != nil
  }

  ...

}
  1. Aggiorna il codice in toggleFavourite per eseguire la query ogni volta che l'utente tocca il pulsante. In questo modo, la proprietà calcolata isFavourite restituisce sempre il valore corretto.
  private func toggleFavourite() {
    Task {
      if isFavourite {
        ...
      }

      let _ = try await isFavouriteRef.execute()
    }
  }

Recuperare i film preferiti

Come ultimo passaggio per questa funzionalità, implementerai il recupero dei film preferiti dell'utente in modo che possa vederli nella sua lista di visualizzazione.

  1. In VS Code, apri queries.gql in dataconnect/connector/queries.gql e incolla la seguente query:
## Get favorite movies by user ID
query GetUserFavoriteMovies @auth(level: USER) {
  user(id_expr: "auth.uid") {
    favoriteMovies: favorite_movies_on_user {
      movie {
        id
        title
        genre
        imageUrl
        releaseYear
        rating
        description
      }
    }
  }
}

L'elenco dei film preferiti dell'utente viene visualizzato su LibraryScreen. Questa schermata deve mostrare i dati solo se l'utente ha eseguito l'accesso, quindi devi prima connettere lo stato di autenticazione della schermata a AuthenticationService dell'app.

  1. Aggiungi il codice alla mappa da FavoriteMovieFavoriteMovies a Movie a Movie+DataConnect.swift:
import FirebaseDataConnect
import FriendlyFlixSDK

extension Movie {

  ...

  init(from: GetUserFavoriteMoviesQuery.Data.User.FavoriteMovieFavoriteMovies) {
    id = from.movie.id
    title = from.movie.title
    description = from.movie.description ?? ""
    releaseYear = from.movie.releaseYear
    rating = from.movie.rating
    imageUrl = from.movie.imageUrl
  }
}
  1. In Xcode, apri LibraryScreen, quindi aggiorna isSignedIn nel seguente modo:
struct LibraryScreen: View {
  ...

  private var isSignedIn: Bool {
    authenticationService.user != nil
  }

}
  1. Poi, importa Firebase Data Connect e FriendlyFlixSDK e ottieni un riferimento alla query GetUserFavoriteMovies:
import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

struct LibraryScreen {

 ...

  private var connector = DataConnect.friendlyFlixConnector

  ...

  init() {
    watchListRef = connector.getUserFavoriteMoviesQuery.ref()
  }

  private let watchListRef: QueryRefObservation<
    GetUserFavoriteMoviesQuery.Data,
    GetUserFavoriteMoviesQuery.Variables
  >
  private var watchList: [Movie] {
    watchListRef.data?.user?.favoriteMovies.map(Movie.init) ?? []
  }

  ...

}


  1. Assicurati che la query watchListRef venga eseguita quando viene visualizzata la visualizzazione:
extension LibraryScreen: View {
  var body: some View {
    ...
            MovieListSection(namespace: namespace, title: "Watch List", movies: watchList)
              .onAppear {
                Task {
                  try await watchListRef.execute()
                }
  ...

Guarda come funziona

Ora puoi eseguire l'app e provare la funzionalità dei preferiti che hai appena implementato. Un paio di cose da tenere a mente:

  • Assicurati che l'emulatore Firebase sia in esecuzione
  • Assicurati di aver aggiunto dati simulati per i film e i relativi dettagli.
  • Assicurati di esserti registrato come utente
  1. In Xcode, fai clic sul pulsante Esegui per avviare l'app sul simulatore iOS.
  2. Una volta avviata l'app, tocca la scheda di un film per visualizzarne i dettagli.
  3. Tocca l'icona a forma di cuore per contrassegnare il film come preferito. Il cuore dovrebbe diventare pieno.
  4. Ripeti l'operazione per un paio di film.
  5. Vai alla scheda Raccolta. Ora dovresti vedere un elenco di tutti i film che hai contrassegnato come preferiti.

10. Complimenti

Congratulazioni, hai aggiunto Firebase Data Connect a un'app per iOS. Ora conosci i passaggi chiave necessari per configurare Data Connect, creare query e mutazioni e gestire l'autenticazione utente.

(Facoltativo) Esegui il deployment in produzione

Finora questa app ha utilizzato solo gli emulatori Firebase. Se vuoi scoprire come eseguire il deployment di questa app in un progetto Firebase reale, vai al passaggio successivo.

11. (Facoltativo) Esegui il deployment dell'app

Finora questa app è stata completamente locale e tutti i dati sono contenuti in Firebase Emulator Suite. In questa sezione imparerai a configurare il progetto Firebase in modo che questa app funzioni in produzione.

Attivare Firebase Authentication

  1. Nella console Firebase, vai alla sezione Autenticazione e fai clic su Inizia.
  2. Vai alla scheda Metodo di accesso .
  3. Seleziona l'opzione Email/Password dalla sezione dei provider nativi.
  4. Attiva il provider email/password, quindi fai clic su Salva.

Attivare Firebase Data Connect

Importante: se è la prima volta che esegui il deployment di uno schema nel tuo progetto, questo processo creerà un'istanza Cloud SQL PostgreSQL, che può richiedere circa 15 minuti. Non potrai eseguire il deployment finché l'istanza Cloud SQL non sarà pronta e integrata con Firebase Data Connect.

1. Nell'interfaccia utente dell'estensione VS Code di Firebase Data Connect, fai clic su Esegui il deployment in produzione. 2. Potresti dover esaminare le modifiche allo schema e approvare quelle potenzialmente distruttive. Ti verrà chiesto di: - Esaminare le modifiche allo schema utilizzando firebase dataconnect:sql:diff - Quando le modifiche ti soddisfano, applicale utilizzando il flusso avviato da firebase dataconnect:sql:migrate

L'istanza Cloud SQL per PostgreSQL verrà aggiornata con lo schema e i dati di cui è stato eseguito il deployment finale. Puoi monitorare lo stato nella console Firebase.

Ora puoi fare clic su Esegui (produzione) nel riquadro Firebase Data Connect, proprio come hai fatto con gli emulatori locali, per aggiungere dati all'ambiente di produzione.

Prima di eseguire di nuovo l'app per iOS, assicurati che si connetta all'istanza di produzione del tuo progetto:

  1. Apri il menu Product > Scheme > Edit Scheme… (Prodotto > Schema > Modifica schema…).
  2. Nella sezione Esegui, deseleziona l'argomento di avvio -useEmulator YES.