Codelab do Firebase para iOS (Swift)

1. Visão geral

2efe6805ef369641.png

Este é o codelab do Friendly Chat. Neste codelab, você vai aprender a usar a plataforma do Firebase para criar aplicativos iOS. Você vai implementar um cliente de chat e monitorar o desempenho dele usando o Firebase.

O que você aprenderá

  • Permitir que os usuários façam login.
  • Sincronize dados usando o Firebase Realtime Database.
  • Armazene arquivos binários no Firebase Storage.

O que é necessário

  • Xcode
  • CocoaPods
  • Um dispositivo de teste com iOS 8.0 ou mais recente ou um simulador

Como você usará este tutorial?

Apenas leitura Leitura e exercícios

Como você classificaria sua experiência com a criação de apps iOS?

Iniciante Intermediário Proficiente

2. Acessar o exemplo de código

Clone o repositório do GitHub na linha de comando.

$ git clone https://github.com/firebase/codelab-friendlychat-ios

3. Criar o app inicial

2f4c98d858c453fe.png

Para criar o app inicial:

  1. Em uma janela de terminal, navegue até o diretório android_studio_folder.pngios-starter/swift-starter no download do exemplo de código.
  2. Executar pod install --repo-update
  3. Abra o arquivo FriendlyChatSwift.xcworkspace para abrir o projeto no Xcode.
  4. Clique no botão 98205811bbed9d74.pngExecutar.

A tela inicial do Friendly Chat vai aparecer depois de alguns segundos. A interface vai aparecer. No entanto, não é possível fazer login, enviar ou receber mensagens. O app será interrompido com uma exceção até que você conclua a próxima etapa.

4. Configurar um projeto do Firebase

Criar um projeto do Firebase

  1. Faça login no console do Firebase usando sua Conta do Google.
  2. Clique no botão para criar um projeto e insira um nome (por exemplo, FriendlyChat).
  3. Clique em Continuar.
  4. Se solicitado, leia e aceite os Termos do Firebase e clique em Continuar.
  5. (Opcional) Ative a assistência de IA no console do Firebase (chamada de "Gemini no Firebase").
  6. Neste codelab, você não precisa do Google Analytics. Portanto, desative a opção do Google Analytics.
  7. Clique em Criar projeto, aguarde o provisionamento e clique em Continuar.

Fazer upgrade do plano de preços do Firebase

Para usar o Cloud Storage para Firebase, seu projeto do Firebase precisa estar no plano de preços de pagamento por uso (Blaze), o que significa que ele está vinculado a uma conta do Cloud Billing.

  • Uma conta do Cloud Billing exige uma forma de pagamento, como cartão de crédito.
  • Se você ainda não conhece o Firebase e o Google Cloud, confira se tem qualificação para receber um crédito de US$300 e uma conta de teste sem custos financeiros do Cloud Billing.
  • Se você estiver fazendo este codelab como parte de um evento, pergunte ao organizador se há créditos do Cloud disponíveis.

Para fazer upgrade do seu projeto para o plano Blaze, siga estas etapas:

  1. No console do Firebase, selecione Fazer upgrade do seu plano.
  2. Selecione o plano Blaze. Siga as instruções na tela para vincular uma conta do Cloud Billing ao seu projeto.
    Se você precisou criar uma conta do Cloud Billing como parte desse upgrade, talvez seja necessário voltar para o fluxo de upgrade no console do Firebase para concluir o processo.

Conectar seu app iOS

  1. Na tela "Visão geral do projeto" do novo projeto, clique em Adicionar o Firebase ao app para iOS.
  2. Insira o ID do pacote como "com.google.firebase.codelab.FriendlyChatSwift".
  3. Insira o ID da App Store como "123456".
  4. Clique em Registrar app.

Adicionar o arquivo GoogleService-Info.plist ao app

Na segunda tela, clique em Fazer o download do GoogleService-Info.plist para baixar um arquivo de configuração que contém todos os metadados necessários do Firebase para seu app. Copie esse arquivo para seu aplicativo e adicione-o ao destino FriendlyChatSwift.

Agora, clique no "x" no canto superior direito do pop-up para fechar e pule as etapas 3 e 4, já que você vai realizar essas etapas aqui.

19d59efb213ddbdc.png

Importar módulo do Firebase

Primeiro, verifique se o módulo Firebase foi importado.

AppDelegate.swift, FCViewController.swift

import Firebase

Configurar o Firebase no AppDelegate

Use o método "configure" em FirebaseApp dentro da função application:didFinishLaunchingWithOptions para configurar os serviços do Firebase subjacentes no arquivo .plist.

AppDelegate.swift

  func application(_ application: UIApplication, didFinishLaunchingWithOptions
      launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  FirebaseApp.configure()
  GIDSignIn.sharedInstance().delegate = self
  return true
}

5. Identificar usuários

Usar regras para restringir a usuários autenticados

Agora vamos adicionar uma regra para exigir autenticação antes de ler ou gravar mensagens. Para isso, adicionamos as seguintes regras ao objeto de dados de mensagens. Na seção "Banco de dados" do Console do Firebase, selecione "Realtime Database" e clique na guia "Regras". Em seguida, atualize as regras para que fiquem assim:

{
  "rules": {
    "messages": {
      ".read": "auth != null",
      ".write": "auth != null"
    }
  }
}

Para mais informações sobre como isso funciona, incluindo documentação sobre a variável "auth", consulte a documentação de segurança do Firebase.

Configurar APIs de autenticação

Antes que seu aplicativo possa acessar as APIs do Firebase Authentication em nome dos usuários, você precisa ativar o serviço.

  1. Navegue até o console do Firebase e selecione seu projeto.
  2. Selecione Autenticação.
  3. Selecione a guia Método de login.
  4. Ative a opção Google (azul).
  5. Clique em Salvar na caixa de diálogo resultante.

Se você receber erros mais tarde neste codelab com a mensagem "CONFIGURATION_NOT_FOUND", volte a esta etapa e verifique seu trabalho.

Confirmar a dependência do Firebase Auth

Confirme se as dependências do Firebase Auth estão no arquivo Podfile.

Podfile

pod 'Firebase/Auth'

Configure o arquivo Info.plist para o Login do Google.

Você precisará adicionar um esquema de URL personalizado ao seu projeto do Xcode.

  1. Abra a configuração do projeto: clique duas vezes no nome dele na visualização em árvore à esquerda. Selecione seu app na seção "DESTINOS", depois a guia "Informações" e expanda a seção "Tipos de URL".
  2. Clique no botão + e adicione um esquema de URL ao ID do cliente invertido. Para encontrar esse valor, abra o arquivo de configuração GoogleService-Info.plist e procure a chave REVERSED_CLIENT_ID. Copie e cole o valor da chave na caixa "Esquemas de URL" na página de configuração. Deixe os outros campos em branco.
  3. Quando concluída, a configuração será semelhante à mostrada a seguir, mas com os valores específicos do seu app:

1b54d5bd2f4f1448.png

Definir o clientID para o Login do Google

Depois que o Firebase for configurado, poderemos usar o clientID para configurar o Google Sign-In no método "didFinishLaunchingWithOptions:".

AppDelegate.swift

  func application(_ application: UIApplication, didFinishLaunchingWithOptions
      launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  FirebaseApp.configure()
  GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
  GIDSignIn.sharedInstance().delegate = self
  return true
}

Adicionar o gerenciador de login

Depois que o resultado do login com o Google for bem-sucedido, use a conta para fazer a autenticação com o Firebase.

AppDelegate.swift

  func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
    if let error = error {
      print("Error \(error)")
      return
    }

    guard let authentication = user.authentication else { return }
    let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                      accessToken: authentication.accessToken)
    Auth.auth().signIn(with: credential) { (user, error) in
      if let error = error {
        print("Error \(error)")
        return
      }
    }
  }

Fazer login automático do usuário. Em seguida, adicione um listener ao Firebase Auth para permitir que o usuário entre no app após o login. E remova o listener na desinicialização.

SignInViewController.swift

  override func viewDidLoad() {
    super.viewDidLoad()
    GIDSignIn.sharedInstance().uiDelegate = self
    GIDSignIn.sharedInstance().signInSilently()
    handle = Auth.auth().addStateDidChangeListener() { (auth, user) in
      if user != nil {
        MeasurementHelper.sendLoginEvent()
        self.performSegue(withIdentifier: Constants.Segues.SignInToFp, sender: nil)
      }
    }
  }

  deinit {
    if let handle = handle {
      Auth.auth().removeStateDidChangeListener(handle)
    }
  }

Sair

Adicionar o método de encerramento da sessão

FCViewController.swift

  @IBAction func signOut(_ sender: UIButton) {
    let firebaseAuth = Auth.auth()
    do {
      try firebaseAuth.signOut()
      dismiss(animated: true, completion: nil)
    } catch let signOutError as NSError {
      print ("Error signing out: \(signOutError.localizedDescription)")
    }
  }

Testar a leitura de mensagens como usuário conectado

  1. Clique no botão 98205811bbed9d74.pngExecutar.
  2. Você será redirecionado imediatamente para a tela de login. Toque no botão "Fazer login com o Google".
  3. Se tudo funcionar bem, você vai para a tela de mensagens.

6. Ativar o Realtime Database

2efe6805ef369641.png

Importar mensagens

No seu projeto no console do Firebase, selecione o item Banco de dados na barra de navegação à esquerda. No menu flutuante do banco de dados, selecione Importar JSON. Procure o arquivo initial_messages.json no diretório friendlychat, selecione-o e clique no botão Importar. Isso vai substituir todos os dados atuais no seu banco de dados. Você também pode editar o banco de dados diretamente, usando o + verde e o x vermelho para adicionar e remover itens.

20ccf4856b715b4c.png

Depois da importação, seu banco de dados vai ficar assim:

f3e0367f1c9cd187.png

Confirmar a dependência do Firebase Database

No bloco de dependências do arquivo Podfile, confirme se Firebase/Database está incluído.

Podfile

pod 'Firebase/Database'

Sincronizar mensagens atuais

Adicione código que sincroniza as mensagens recém-adicionadas à interface do app.

O código adicionado nesta seção vai:

  • Inicialize o banco de dados do Firebase e adicione um listener para processar as mudanças feitas nele.
  • Atualize o DataSnapshot para que as novas mensagens sejam mostradas.

Modifique os métodos "deinit", "configureDatabase" e "tableView:cellForRow indexPath:" do FCViewController. Substitua pelo código definido abaixo:

FCViewController.swift

  deinit {
    if let refHandle = _refHandle {
      self.ref.child("messages").removeObserver(withHandle: _refHandle)
    }
  }


  func configureDatabase() {
    ref = Database.database().reference()
    // Listen for new messages in the Firebase database
    _refHandle = self.ref.child("messages").observe(.childAdded, with: { [weak self] (snapshot) -> Void in
      guard let strongSelf = self else { return }
      strongSelf.messages.append(snapshot)
      strongSelf.clientTable.insertRows(at: [IndexPath(row: strongSelf.messages.count-1, section: 0)], with: .automatic)
    })
  }


  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // Dequeue cell
    let cell = self.clientTable.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
    // Unpack message from Firebase DataSnapshot
    let messageSnapshot = self.messages[indexPath.row]
    guard let message = messageSnapshot.value as? [String: String] else { return cell }
    let name = message[Constants.MessageFields.name] ?? ""
    let text = message[Constants.MessageFields.text] ?? ""
    cell.textLabel?.text = name + ": " + text
    cell.imageView?.image = UIImage(named: "ic_account_circle")
    if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
        let data = try? Data(contentsOf: URL) {
      cell.imageView?.image = UIImage(data: data)
    }
    return cell
  }

Teste a sincronização das mensagens

  1. Clique no botão 98205811bbed9d74.pngExecutar.
  2. Clique no botão Fazer login para começar e acessar a janela de mensagens.
  3. Adicione novas mensagens diretamente no Console do Firebase clicando no símbolo de mais verde ao lado da entrada "mensagens" e adicionando um objeto como o seguinte: f9876ffc8b316b14.png
  4. Confirme se elas aparecem na interface do Friendly Chat.

7. Enviar mensagens

Implementar o envio de mensagens

Envie valores para o banco de dados. Quando você usa o método push para adicionar dados ao Firebase Realtime Database, um ID automático é adicionado. Esses IDs gerados automaticamente são sequenciais, o que garante que as novas mensagens sejam adicionadas na ordem correta.

Modifique o método "sendMessage:" do FCViewController e substitua pelo código definido abaixo:

FCViewController.swift

  func sendMessage(withData data: [String: String]) {
    var mdata = data
    mdata[Constants.MessageFields.name] = Auth.auth().currentUser?.displayName
    if let photoURL = Auth.auth().currentUser?.photoURL {
      mdata[Constants.MessageFields.photoURL] = photoURL.absoluteString
    }

    // Push data to Firebase Database
    self.ref.child("messages").childByAutoId().setValue(mdata)
  }

Testar o envio de mensagens

  1. Clique no botão 98205811bbed9d74.pngExecutar.
  2. Clique em Fazer login para acessar a janela de mensagens.
  3. Digite uma mensagem e clique em "Enviar". A nova mensagem vai aparecer na interface do app e no Console do Firebase.

8. Armazenar e receber imagens

Confirmar dependência do Firebase Storage

No bloco de dependências do Podfile, confirme se Firebase/Storage está incluído.

Podfile

pod 'Firebase/Storage'

Configurar o Cloud Storage para Firebase

Veja como configurar o Cloud Storage para Firebase no seu projeto do Firebase:

  1. No painel à esquerda do console do Firebase, expanda Build e selecione Storage.
  2. Clique em Começar.
  3. Selecione um local para seu bucket de armazenamento padrão.
    Os buckets em US-WEST1, US-CENTRAL1 e US-EAST1 podem aproveitar o nível"Sempre sem custo financeiro" do Google Cloud Storage. Os buckets em todos os outros locais seguem os preços e usos do Google Cloud Storage.
  4. Clique em Iniciar no modo de teste. Leia o aviso sobre as regras de segurança.
    Mais adiante neste codelab, você vai adicionar regras de segurança para proteger seus dados. Não distribua ou exponha um aplicativo publicamente sem adicionar regras de segurança ao bucket do Storage.
  5. Clique em Criar.

Configurar o FirebaseStorage

FCViewController.swift

  func configureStorage() {
    storageRef = Storage.storage().reference()
  }

Receber imagens em mensagens atuais

Adicione código que faça o download de imagens do Firebase Storage.

Modifique o método "tableView: cellForRowAt indexPath:" do FCViewController e substitua pelo código definido abaixo:

FCViewController.swift

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // Dequeue cell
    let cell = self.clientTable .dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
    // Unpack message from Firebase DataSnapshot
    let messageSnapshot: DataSnapshot! = self.messages[indexPath.row]
    guard let message = messageSnapshot.value as? [String:String] else { return cell }
    let name = message[Constants.MessageFields.name] ?? ""
    if let imageURL = message[Constants.MessageFields.imageURL] {
      if imageURL.hasPrefix("gs://") {
        Storage.storage().reference(forURL: imageURL).getData(maxSize: INT64_MAX) {(data, error) in
          if let error = error {
            print("Error downloading: \(error)")
            return
          }
          DispatchQueue.main.async {
            cell.imageView?.image = UIImage.init(data: data!)
            cell.setNeedsLayout()
          }
        }
      } else if let URL = URL(string: imageURL), let data = try? Data(contentsOf: URL) {
        cell.imageView?.image = UIImage.init(data: data)
      }
      cell.textLabel?.text = "sent by: \(name)"
    } else {
      let text = message[Constants.MessageFields.text] ?? ""
      cell.textLabel?.text = name + ": " + text
      cell.imageView?.image = UIImage(named: "ic_account_circle")
      if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
          let data = try? Data(contentsOf: URL) {
        cell.imageView?.image = UIImage(data: data)
      }
    }
    return cell
  }

9. Enviar mensagens com imagens

Implementar o armazenamento e envio de imagens

Faça upload de uma imagem do usuário e sincronize o URL de armazenamento dela com o banco de dados para que ela seja enviada na mensagem.

Modifique o método "imagePickerController: didFinishPickingMediaWithInfo:" do FCViewController. Substitua pelo código definido abaixo:

FCViewController.swift

  func imagePickerController(_ picker: UIImagePickerController,
    didFinishPickingMediaWithInfo info: [String : Any]) {
      picker.dismiss(animated: true, completion:nil)
    guard let uid = Auth.auth().currentUser?.uid else { return }

    // if it's a photo from the library, not an image from the camera
    if #available(iOS 8.0, *), let referenceURL = info[UIImagePickerControllerReferenceURL] as? URL {
      let assets = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
      let asset = assets.firstObject
      asset?.requestContentEditingInput(with: nil, completionHandler: { [weak self] (contentEditingInput, info) in
        let imageFile = contentEditingInput?.fullSizeImageURL
        let filePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\((referenceURL as AnyObject).lastPathComponent!)"
        guard let strongSelf = self else { return }
        strongSelf.storageRef.child(filePath)
          .putFile(from: imageFile!, metadata: nil) { (metadata, error) in
            if let error = error {
              let nsError = error as NSError
              print("Error uploading: \(nsError.localizedDescription)")
              return
            }
            strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
          }
      })
    } else {
      guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else { return }
      let imageData = UIImageJPEGRepresentation(image, 0.8)
      let imagePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
      let metadata = StorageMetadata()
      metadata.contentType = "image/jpeg"
      self.storageRef.child(imagePath)
        .putData(imageData!, metadata: metadata) { [weak self] (metadata, error) in
          if let error = error {
            print("Error uploading: \(error)")
            return
          }
          guard let strongSelf = self else { return }
          strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
      }
    }
  }

Testar o envio e o recebimento de mensagens com imagens

  1. Clique no botão 98205811bbed9d74.pngExecutar.
  2. Clique em Fazer login para acessar a janela de mensagens.
  3. Clique no ícone "Adicionar uma foto" para escolher uma imagem. A nova mensagem com a foto vai aparecer na interface do app e no console do Firebase.

10. Parabéns!

Você usou o Firebase para criar facilmente um aplicativo de chat em tempo real.

O que vimos

  • Realtime Database
  • Login federado
  • Armazenamento

Saiba mais