Criar com o Firebase Data Connect (iOS / Swift)

1. Visão geral

Este codelab orienta você no processo de integração do Firebase Data Connect com um banco de dados do Cloud SQL para criar um app de análise de filmes para iOS usando o SwiftUI.

Você vai aprender a conectar seu aplicativo iOS a um banco de dados do Cloud SQL usando o Firebase Data Connect, ativando a sincronização de dados perfeita para resenhas de filmes.

Ao final deste codelab, você terá um app funcional para iOS que permite que os usuários naveguem e marquem filmes como favoritos, tudo isso com o suporte de um banco de dados do Cloud SQL usando o poder do Firebase Data Connect.

O que você vai aprender

Neste codelab, você vai aprender a:

  • Configure o Firebase Data Connect usando o pacote do Firebase Emulator para agilizar o processo.
  • Projetar um esquema de banco de dados usando o Data Connect e o GraphQL.
  • Crie um SDK Swift com segurança de tipo usando o esquema do banco de dados e adicione-o a um aplicativo Swift.
  • Implemente a autenticação do usuário e integre-a ao Firebase Data Connect para proteger os dados dos seus usuários.
  • Extraia, atualize, exclua e gerencie dados no Cloud SQL usando consultas e mutações com tecnologia do GraphQL.
  • (Opcional) Implantar um serviço do Data Connect para produção.

Pré-requisitos

  • A versão mais recente do Xcode
  • O código de exemplo do codelab. Você vai fazer o download do código de exemplo em uma das primeiras etapas do codelab.

2. Configurar o projeto de exemplo

Criar um projeto do Firebase

  1. Faça login no console do Firebase com sua Conta do Google.
  2. No Console do Firebase, clique em Criar um projeto do Firebase.
  3. Insira um nome para seu projeto do Firebase (por exemplo, "Friendly Flix") e clique em Continuar.
  4. Talvez seja necessário ativar a assistência de IA para seu projeto do Firebase. Para os fins deste codelab, sua seleção não importa.
  5. Talvez seja necessário ativar o Google Analytics. Para os fins deste codelab, sua seleção não importa.
  6. Depois de aproximadamente um minuto, o projeto do Firebase estará pronto. Clique em Continuar.

Fazer o download do código

Execute o comando a seguir para clonar o código de exemplo deste codelab. Isso vai criar um diretório chamado codelab-dataconnect-ios na sua máquina:

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

Se você não tiver o git na sua máquina, também poderá fazer o download do código diretamente do GitHub.

Adicionar a configuração do Firebase

O SDK do Firebase usa um arquivo de configuração para se conectar ao seu projeto do Firebase. Nas plataformas Apple, esse arquivo é chamado de GoogleServices-Info.plist. Nesta etapa, você vai fazer o download do arquivo de configuração e adicioná-lo ao projeto do Xcode.

  1. No Console do Firebase, selecione Visão geral do projeto no painel de navegação à esquerda.
  2. Clique no botão iOS+ para selecionar a plataforma. Quando o ID do pacote da Apple for solicitado, use com.google.firebase.samples.FriendlyFlix.
  3. Clique em Registrar app e siga as instruções para fazer o download do arquivo GoogleServices-Info.plist.
  4. Mova o arquivo salvo para o diretório start/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/ do código que você acabou de salvar, substituindo o arquivo GoogleServices-Info.plist.
  5. Em seguida, clique em Próxima algumas vezes para concluir a configuração no console do Firebase. Não é necessário adicionar o SDK ao app, porque isso já foi feito no projeto inicial.
  6. Por fim, clique em Continuar para o console para concluir o processo de configuração.

3. Configurar o Data Connect

Instalação

Instalação automática

Execute o seguinte comando no diretório codelab-dataconnect-ios/FriendlyFlix:

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

Esse script tenta configurar o ambiente de desenvolvimento para você e iniciar um ambiente de desenvolvimento integrado (IDE) baseado em navegador. Esse ambiente de desenvolvimento integrado oferece ferramentas, incluindo uma extensão pré-empacotada do VS Code, para ajudar a gerenciar seu esquema e definir consultas e mutações a serem usadas no aplicativo, além de gerar SDKs com tipagem forte.

Depois de executar o script, o VS Code será aberto automaticamente.

Depois de fazer isso uma vez, você pode iniciar o VS Code executando-o no diretório local:

code .

Instalação manual

  1. Instalar o Visual Studio Code
  2. Instale o Node.js.
  3. No VS Code, abra o diretório codelab-dataconnect-ios/FriendlyFlix.
  4. Instale a extensão Firebase Data Connect no Visual Studio Code Marketplace.

Inicializar o Data Connect no projeto

No painel à esquerda, clique no ícone do Firebase para abrir a interface da extensão do Data Connect no VS Code.

  1. Clique no botão Fazer login com o Google. Uma janela do navegador será aberta. Siga as instruções para fazer login na extensão com sua Conta do Google.
  2. Clique no botão Conectar um projeto do Firebase e selecione o projeto que você criou anteriormente no console.
  3. Clique no botão Run firebase init e siga as etapas no terminal integrado.

Configurar a geração do SDK

Depois de clicar no botão Run firebase init, a extensão do Firebase Data Connect vai inicializar um diretório dataconnect para você.

No VS Code, abra o arquivo dataconnect/connector/connector.yaml para encontrar a configuração padrão.

Atualize a configuração e use as configurações a seguir para garantir que o código gerado funcione com este codelab. Especificamente, verifique se connectorId está definido como friendly-flix e o pacote Swift como FriendlyFlixSDK.

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

Confira o que essas configurações significam:

  • connectorId: um nome exclusivo para esse conector.
  • outputDir: caminho em que o SDK do Data Connect gerado vai ser armazenado. Esse caminho é relativo ao diretório que contém o arquivo connector.yaml.
  • package: o nome do pacote a ser usado para o pacote Swift gerado.

Depois de salvar o arquivo, o Firebase Data Connect vai gerar um pacote Swift chamado FriendlyFlixSDK e colocá-lo ao lado da pasta do projeto FriendlyFlix.

Iniciar os emuladores do Firebase

No VS Code, mude para a Visualização do Firebase e clique no botão Iniciar emuladores.

Isso vai iniciar o Firebase Emulator no terminal integrado. O resultado será semelhante a este:

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

Adicionar o pacote gerado ao app Swift

  1. Abrir FriendlyFlix/app/FriendlyFlix/FriendlyFlix.xcodeproj no Xcode
  2. Selecione File > Add Package Dependencies….
  3. Clique em Add Local… e adicione o pacote FriendlyFlixSDK da pasta FriendlyFlix/app.
  4. Aguarde o Xcode resolver as dependências do pacote.
  5. Na caixa de diálogo Choose Package Products for FriendlyFlixSDK, selecione FriendlyFlix como destino e clique em Add Package.

Configurar o app iOS para usar o emulador local

  1. Abra FriendlyFlixApp.swift. Você pode pressionar CMD + Shift + O para abrir a caixa de diálogo Abrir rapidamente e digitar "FriendlyFlixApp" para encontrar o arquivo rapidamente.
  2. Importar o Firebase, o Firebase Auth, o Firebase Data Connect e o SDK gerado para seu esquema
  3. No inicializador, configure o Firebase.
  4. Verifique se o DataConnect e o Firebase Auth usam o emulador local.
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. Selecione um simulador do iOS no menu suspenso Destino.
  2. Pressione CMD+R (ou clique no botão Run) no Xcode para executar o app em um simulador.

4. Definir o esquema e pré-preencher o banco de dados

Nesta seção, você vai definir a estrutura e as relações entre as principais entidades do aplicativo de filmes em um esquema. Entidades como Movie, MovieMetaData e outras são mapeadas para tabelas de banco de dados, com relacionamentos estabelecidos usando o Firebase Data Connect e as diretivas de esquema do GraphQL.

Entidades e relacionamentos principais

O modelo de dados desse app de rastreamento de filmes consiste em várias entidades que você vai criar ao longo deste codelab. Você vai criar as entidades principais primeiro e, à medida que implementar mais e mais recursos, vai adicionar as entidades necessárias para esses recursos.

Nesta etapa, você vai criar os tipos Movie e MovieMetadata.

Filme

O tipo Movie define a estrutura principal de uma entidade de filme, incluindo campos como title, genre, releaseYear e rating.

No VS Code, adicione a definição de 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

O tipo MovieMetadata estabelece uma relação de um para um com o tipo Movie. Ele inclui dados adicionais, como o diretor do filme.

Adicione a definição da tabela MovieMetadata ao arquivo dataconnect/schema/schema.gql:

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

Campos e padrões gerados automaticamente

O esquema usa expressões como @default(expr: "uuidV4()") para gerar automaticamente IDs e carimbos de data/hora exclusivos. Por exemplo, o campo id no tipo Movie é preenchido automaticamente com um UUID quando um novo registro é criado.

Inserir dados simulados para filmes e metadados de filmes

Com o esquema definido, agora você pode pré-preencher o banco de dados com dados fictícios para testes.

  1. No Finder, copie finish/FriendlyFlix/dataconnect/moviedata_insert.gql para a pasta start/FriendlyFlix/dataconnect.
  2. No VS Code, abra dataconnect/moviedata_insert.gql.
  3. Verifique se os emuladores na extensão do Firebase Data Connect estão em execução.
  4. Você vai encontrar um botão Run (local) na parte de cima do arquivo. Clique nele para inserir os dados fictícios do filme no banco de dados.
  5. Verifique o terminal Data Connect Execution para confirmar que os dados foram adicionados.

Com os dados em vigor, siga para a próxima etapa e aprenda a criar consultas no Data Connect.

5. Extrair e mostrar filmes

Nesta seção, você vai implementar um recurso para mostrar uma lista de filmes.

Primeiro, você vai aprender a criar uma consulta que extrai todos os filmes da tabela movies. O Firebase Data Connect gera código para um SDK com segurança de tipo que pode ser usado para executar a consulta e mostrar os filmes recuperados na interface do app.

Definir a consulta "ListMovies"

As consultas no Firebase Data Connect são gravadas em GraphQL, permitindo que você especifique quais campos serão buscados. No FriendlyFlix, as telas que mostram filmes exigem os seguintes campos: title, description, releaseYear, rating e imageUrl. Além disso, como este é um app SwiftUI, você vai precisar do id para ajudar com a identidade da visualização do SwiftUI.

No VS Code, abra o arquivo dataconnect/connector/queries.gql e adicione a consulta ListMovies:

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

Para testar a nova consulta, clique no botão Run (local) para executar a consulta no seu banco de dados local. A lista de filmes do banco de dados vai aparecer na seção Results do terminal de execução do Data Connect.

Conectar a consulta ListMovies à tela inicial do app

Agora que você testou a consulta no emulador do Data Connect, é possível chamar a consulta no seu app.

Quando você salva queries.gql, o Firebase Data Connect gera o código correspondente à consulta ListMovies no pacote FriendlyFlixSDK.

No Xcode, abra Movie+DataConnect.swift e adicione o seguinte código para mapear de ListMoviesQuery.Data.Movie para 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
  }
}

Abra o arquivo HomeScreen.swift e atualize-o usando o snippet de código abaixo.

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) ?? []
  }

  ...
}

A consulta listMoviesQuery() foi gerada pelo Data Connect quando você salvou queries.gql. Para conferir a implementação em Swift, confira o arquivo FriendlyFlixOperations.swift no pacote FriendlyFlixSDK.

Execute o aplicativo

No Xcode, clique no botão Run para iniciar o app no Simulador do iOS.

Quando o app for iniciado, você verá uma tela como esta:

Todas as áreas do app (a seção principal, os principais filmes e a lista de filmes) mostram a mesma lista. Isso ocorre porque você está usando a mesma consulta para todas essas visualizações. Nas próximas seções, você vai implementar consultas personalizadas.

6. Mostrar o herói e os principais filmes

Nesta etapa, você vai se concentrar em atualizar a forma como os filmes são exibidos na seção principal, que é o carrossel em destaque na parte de cima da tela inicial, e também na seção de filmes mais assistidos abaixo.

No momento, a consulta "ListMovies" recupera todos os filmes. Para otimizar a exibição dessas seções, limite o número de filmes retornados por consulta. A implementação atual da consulta ListMovies ainda não oferece suporte integrado para limitar resultados. Você vai adicionar o suporte para limitar e ordenar nessa seção.

Melhorar a consulta ListMovies

Abra queries.gql e atualize ListMovies da seguinte maneira para adicionar suporte ao pedido e limitação:

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

Isso permite limitar o número de filmes que a consulta retorna e ordenar o conjunto de resultados por classificação e ano de lançamento.

Depois de salvar esse arquivo, o Firebase Data Connect vai gerar o código novamente automaticamente no FriendlyFlixSDK. Na próxima etapa, você vai atualizar o código em HomeScreen.swift para usar esses recursos adicionais.

Usar a consulta aprimorada na interface

Volte para o Xcode para fazer as mudanças necessárias em HomeScreen.swift.

Primeiro, atualize heroMoviesRef para buscar os três filmes mais recentes:

struct HomeScreen {
  ...

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

  }
}

Em seguida, configure outra referência de consulta para os principais filmes e defina o filtro para os cinco filmes com as melhores avaliações:

struct HomeScreen {
  ...

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

  init() {
    heroMoviesRef = ...

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

Por fim, atualize a propriedade computada que conecta o resultado dessa consulta à interface:

extension HomeScreen {
  ...

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

}

Confira na prática

Execute o app novamente para conferir os três filmes mais recentes na seção principal e os cinco filmes mais bem avaliados na seção de filmes mais vistos:

7. Mostrar detalhes de filmes e atores

O usuário agora pode procurar filmes. Ao tocar em um card de filme, alguns detalhes sobre ele são mostrados, mas talvez você tenha notado que os detalhes não são muito detalhados.

Isso acontece porque só buscamos os detalhes necessários para renderizar a seção de destaque e a seção de filmes mais vistos: o título do filme, uma breve descrição e o URL da imagem.

Na página de detalhes do filme, vamos mostrar mais informações sobre ele. Nesta seção, você vai melhorar o app para que ele mostre os atores do filme e as críticas na página de detalhes.

Para isso, você precisa fazer algumas coisas:

  • Melhorar o esquema para oferecer suporte a atores e avaliações de filmes
  • Escrever consultas do Firebase Data Connect para buscar detalhes sobre um filme
  • Mostrar os resultados na tela de detalhes do filme

Melhorar o esquema

No VS Code, abra dataconnect/schema/schema.gql e adicione as definições de esquema para 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"
}

Adicionar dados fictícios para atores

Com o esquema atualizado, agora você pode preencher o banco de dados com mais dados fictícios para testes.

  1. No Finder, copie finish/FriendlyFlix/dataconnect/moviededetails_insert.gql para a pasta start/FriendlyFlix/dataconnect.
  2. No VS Code, abra dataconnect/moviededetails_insert.gql.
  3. Verifique se os emuladores na extensão do Firebase Data Connect estão em execução.
  4. Você vai encontrar um botão Run (local) na parte de cima do arquivo. Clique nele para inserir os dados fictícios do filme no banco de dados.
  5. Verifique o terminal de execução da conexão de dados para confirmar se os dados foram adicionados.

Com os dados em mãos, prossiga para a próxima etapa e defina a consulta para buscar os detalhes do filme.

Definir a consulta GetMovieById

No VS Code, abra o arquivo dataconnect/connector/queries.gql e adicione a consulta 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
    }
  }
}

Conectar a consulta GetMovieById à MovieDetailsView

No Xcode, abra o arquivo MovieDetailsView.swift e atualize a propriedade computada movieDetails para que ela corresponda ao seguinte código:

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

Execute o aplicativo

No Xcode, clique no botão Run para iniciar o app no simulador do iOS.

Quando o app for iniciado, toque em um card de filme para mostrar os detalhes. Ele será parecido com o seguinte:

8. Implementar a autenticação do usuário

No momento, o app apresenta informações não personalizadas sobre filmes e atores. Nas etapas a seguir, você vai implementar recursos que associam dados ao usuário conectado. Para começar, permita que os usuários adicionem filmes à lista de interesses pessoal.

Antes de implementar o recurso de lista de observação, é necessário estabelecer a identidade do usuário. Para ativar isso, integre o Firebase Authentication, permitindo que os usuários façam login no app.

Talvez você já tenha notado o botão do avatar do usuário no canto superior direito da tela inicial. Ao tocar nele, você vai acessar uma tela em que os usuários podem se inscrever ou fazer login usando o e-mail e a senha.

Depois que um usuário fizer login, o app vai precisar armazenar os detalhes essenciais dele, principalmente o ID exclusivo e o nome de usuário escolhido.

Ativar o Firebase Authentication

No console do Firebase do seu projeto, acesse a seção "Autenticação" e ative o Firebase Authentication. Em seguida, ative o provedor de autenticação por e-mail/senha.

Na pasta do projeto local, encontre e atualize firebase.json da seguinte maneira para ativar o emulador do Firebase Authentication.

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

Depois disso, é necessário interromper e reiniciar o emulador do Firebase para que a mudança entre em vigor.

Implementar um gerenciador de autenticação

Na próxima seção, você vai implementar a lógica que conecta a autenticação do usuário ao seu banco de dados. Isso envolve a criação de um gerenciador de autenticação que detecta logins bem-sucedidos.

Depois que um usuário é autenticado, esse gerenciador aciona automaticamente a criação da conta correspondente no seu banco de dados.

No Xcode, abra o arquivo AuthenticationService.swift e adicione o seguinte código:

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

Esse é um gerenciador de autenticação genérico que permite usar onSignUp para registrar um fechamento que será chamado quando o usuário fizer login.

Dentro desse fechamento, você pode criar uma nova conta de usuário no banco de dados. Mas, antes disso, você precisa criar uma mutação que permita criar ou atualizar novos usuários no banco de dados.

Adicionar uma entidade de usuário ao esquema

O tipo User define uma entidade de usuário. Os usuários podem interagir com os filmes deixando avaliações ou adicionando filmes aos favoritos.

No VS Code, abra o arquivo dataconnect/schema/schema.gql e adicione a seguinte definição de tabela 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)")
}

Definir uma mutação para inserir ou atualizar um usuário

No VS Code, abra o arquivo dataconnect/connector/mutations.gql e adicione a mutação UpsertUser:

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

Criar um novo usuário após fazer login

No Xcode, abra FriendlyFlixApp.swift e adicione o seguinte código ao inicializador:

@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 {
    ...
  }
}

Esse código usa o upsertUserMutation do Firebase Data Connect gerado para você inserir um novo usuário (ou atualizar um usuário existente com o mesmo ID) sempre que um usuário se inscrever usando o Firebase Authentication.

Confira na prática

Para verificar se isso funciona, primeiro faça login no app para iOS:

  • Se ainda não fez isso, pare e reinicie o emulador do Firebase para garantir que o emulador do Firebase Authentication esteja em execução.
  • No Xcode, clique no botão Run para iniciar o app no simulador do iOS.
  • Clique no ícone de avatar no canto superior direito da tela.
  • Mude para o fluxo de inscrição e faça a inscrição no app.

Em seguida, consulte o banco de dados para verificar se o app criou uma nova conta de usuário para o usuário:

  • No VS Code, abra dataconnect/schema/schema.gql e clique em Ler dados na entidade User.
  • Isso vai criar um novo arquivo de consulta, chamado User_read.gql.
  • Clique em Executar local para conferir todos os usuários na tabela de usuários.
  • No painel "Execução do Data Connect", você vai encontrar uma conta para o usuário que acabou de se inscrever com

9. Gerenciar filmes favoritos

Nesta seção do codelab, você vai implementar interações do usuário no app de análise de filmes, permitindo que os usuários gerenciem os filmes favoritos. Os filmes marcados como favoritos vão aparecer na seção "Lista de interesses" do app.

Melhorar o esquema para oferecer suporte a favoritos

O tipo FavoriteMovie é uma tabela de mesclagem que processa relações muitos para muitos entre usuários e filmes favoritos. Cada tabela vincula uma User a uma Movie.

Copie e cole o snippet de código no arquivo 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!
}

Definir mutações para adicionar e remover favoritos

Antes que o app possa mostrar os filmes favoritos do usuário, ele precisa indicar quais são os favoritos. Para isso, primeiro adicione duas mutações para marcar um filme como um dos favoritos do usuário ou removê-lo dos favoritos novamente.

  1. No VS Code, abra mutations.gql em dataconnect/connector/mutations.gql.
  2. Adicione as seguintes mutações para processar a inclusão de filmes nos favoritos:
## 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 })
}

Conectar as mutações à interface do app

Os usuários podem marcar um filme como favorito clicando no ícone de coração na tela de detalhes do filme.

Para conectar as mutações que você acabou de criar à interface do app, faça as seguintes mudanças em MovieCardView:

  1. Importar o FriendlyFlixSDK e configurar o conector
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. Implemente o método toggleFavourite. Ele será chamado sempre que o usuário tocar no ícone de coração no 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)
      }
    }
  }
}

Isso vai atualizar o estado de favorito do filme atual no banco de dados. Uma etapa final que está faltando é garantir que o estado da interface seja refletido adequadamente.

Definir uma consulta para descobrir se um filme está marcado como favorito

  1. No VS Code, abra queries.gql em dataconnect/connector.
  2. Adicione a consulta abaixo para verificar se um filme está marcado como favorito:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
    movieId
  }
}
  1. No Xcode, crie uma referência à consulta GetIfFavoritedMovie e implemente a propriedade computada que determina se o filme mostrado neste MovieCardView está marcado como favorito para o usuário atual.
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. Atualize o código em toggleFavourite para executar a consulta sempre que o usuário tocar no botão. Isso garante que a propriedade calculada isFavourite sempre retorne o valor correto.
  private func toggleFavourite() {
    Task {
      if isFavourite {
        ...
      }

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

Buscar filmes favoritos

Como etapa final para esse recurso, você vai implementar a busca dos filmes favoritos do usuário para que eles apareçam na lista de títulos assistidos.

  1. No VS Code, abra queries.gql em dataconnect/connector/queries.gql e cole a seguinte consulta:
## 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
      }
    }
  }
}

A lista de filmes favoritos do usuário aparece no LibraryScreen. Essa tela só vai mostrar dados se o usuário tiver feito login. Portanto, primeiro conecte o estado de autenticação da tela ao AuthenticationService do app.

  1. Adicione o código para mapear de FavoriteMovieFavoriteMovies para Movie para 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. No Xcode, abra LibraryScreen e atualize isSignedIn da seguinte maneira:
struct LibraryScreen: View {
  ...

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

}
  1. Em seguida, importe o Firebase Data Connect e o FriendlyFlixSDK e receba uma referência à consulta 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. Confira se a consulta watchListRef é executada quando a visualização aparece:
extension LibraryScreen: View {
  var body: some View {
    ...
            MovieListSection(namespace: namespace, title: "Watch List", movies: watchList)
              .onAppear {
                Task {
                  try await watchListRef.execute()
                }
  ...

Confira na prática

Agora você pode executar o app e testar o recurso de favoritos que acabou de implementar. Alguns pontos importantes:

  • Verifique se o emulador do Firebase está em execução
  • Verifique se você adicionou dados simulados para os filmes e detalhes do filme.
  • Verifique se você se inscreveu como usuário
  1. No Xcode, clique no botão Run para iniciar o app no simulador do iOS.
  2. Quando o app for iniciado, toque em um card de filme para mostrar os detalhes.
  3. Toque no ícone de coração para marcar o filme como favorito. O coração vai ficar sólido.
  4. Repita o processo para alguns filmes.
  5. Acesse a guia "Biblioteca". Agora você vai encontrar uma lista de todos os filmes marcados como favoritos.

10. Parabéns

Parabéns! Você adicionou o Firebase Data Connect a um app iOS. Agora você sabe as principais etapas necessárias para configurar o Data Connect, criar consultas e mutações e processar a autenticação do usuário.

Opcional: implantar na produção

Até agora, esse app só usou os emuladores do Firebase. Se você quiser aprender a implantar esse app em um projeto real do Firebase, siga para a próxima etapa.

11. (Opcional) Implantar o app

Até agora, esse app foi totalmente local, e todos os dados estão contidos no Pacote de emuladores do Firebase. Nesta seção, você vai aprender a configurar seu projeto do Firebase para que o app funcione na produção.

Ativar o Firebase Authentication

  1. No console do Firebase, acesse a seção Autenticação e clique em Começar.
  2. Acesse a guia Método de login .
  3. Selecione a opção "E-mail/senha" na seção de provedores nativos.
  4. Ative o provedor de e-mail/senha e clique em Salvar.

Ativar o Firebase Data Connect

Importante: se esta é a primeira vez que você implanta um esquema no projeto, esse processo vai criar uma instância do PostgreSQL do Cloud SQL, o que pode levar cerca de 15 minutos. Não será possível implantar até que a instância do Cloud SQL esteja pronta e integrada ao Firebase Data Connect.

1. Na interface da extensão do Firebase Data Connect para o VS Code, clique em Deploy to production. 2. Talvez seja necessário analisar as mudanças no esquema e aprovar modificações potencialmente destrutivas. Você vai receber uma solicitação para: - Analisar as mudanças no esquema usando firebase dataconnect:sql:diff - Quando estiver tudo pronto, aplique as mudanças usando o fluxo iniciado por firebase dataconnect:sql:migrate

Sua instância do Cloud SQL para PostgreSQL será atualizada com o esquema e os dados finais implantados. É possível monitorar o status no Console do Firebase.

Agora você pode clicar em Executar (produção) no painel do Firebase Data Connect, assim como fez com os emuladores locais, para adicionar dados ao ambiente de produção.

Antes de executar o app iOS novamente, verifique se ele se conecta à instância de produção do seu projeto:

  1. Abra o menu Produto > Esquema > Editar esquema….
  2. Na seção Run, desmarque o argumento de inicialização -useEmulator YES.