Codelab Swift do Firebase para iOS

1. Visão geral

2efe6805ef369641.png

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

O que você aprenderá

  • Permita que os usuários façam login.
  • Sincronizar dados usando o Firebase Realtime Database.
  • Armazenar arquivos binários no Firebase Storage.

O que é necessário

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

Como você vai usar este tutorial?

Apenas leitura Ler e fazer os 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, acesse 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.pngRun.

A tela inicial do Friendly Chat vai aparecer após alguns segundos. A interface será exibida. No entanto, não é possível fazer login, enviar ou receber mensagens no momento. O app será cancelado com uma exceção até você concluir a próxima etapa.

4. Criar projeto do Console do Firebase

Criar projeto

No Console do Firebase, selecione Adicionar projeto.

Chame o projeto FriendlyChat e clique em Criar projeto.

Captura de tela de 06-11-2015 14:13:39.png

Conectar o 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 salvar um arquivo de configuração que contenha todos os metadados necessários do Firebase para o app. Copie esse arquivo para o aplicativo e adicione-o ao destino FriendlyChatSwift.

Agora você pode clicar no "x" no canto superior direito da janela pop-up para fechá-la (ignorando as etapas 3 e 4) porque realizará essas etapas aqui.

19d59efb213ddbdc.png

Importar módulo do Firebase

Comece verificando se o módulo Firebase foi importado.

AppDelegate.swift, FCViewController.swift

import Firebase

Configurar o Firebase no AppDelegate

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

AppDelegate.swift (link em inglês)

  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 adicionaremos uma regra para exigir autenticação antes de ler ou gravar mensagens. Para isso, adicionamos as seguintes regras ao nosso 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 a documentação sobre a variável "auth"), consulte a documentação de segurança do Firebase.

Configurar APIs do Authentication

Para que seu aplicativo possa acessar as APIs do Firebase Authentication em nome dos usuários, você precisará ativá-las

  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 (azul) a chave Google.
  5. Pressione Salvar na caixa de diálogo exibida

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

Confirmar a dependência do Firebase Auth

Confirme se as dependências do Firebase Auth existem no arquivo Podfile.

Podfile

pod 'Firebase/Auth'

Configure o 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 seu projeto clicando duas vezes no nome dele na visualização em árvore à esquerda. Selecione seu app na seção "DESTINOS". Em seguida, selecione a guia "Informações" e expanda a seção "Tipos de URL".
  2. Clique no botão + e adicione um esquema de URL para seu 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 vai ser semelhante à mostrada a seguir, mas com os valores específicos do seu aplicativo:

1b54d5bd2f4f1448.png

Definir o clientID para o Login do Google

Depois que o Firebase for configurado, poderemos usar o clientID para configurar o Login do Google dentro do método "didFinishLaunchingWithOptions:".

AppDelegate.swift (link em inglês)

  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

Quando o resultado do Login do Google for concluído, use a conta para autenticar com o Firebase.

AppDelegate.swift (link em inglês)

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

Faz o login do usuário automaticamente. 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 ao deinit.

SignInViewController.swift (em inglês)

  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 saída

FCViewController.swift (em inglês)

  @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 após fazer login

  1. Clique no botão 98205811bbed9d74.pngRun.
  2. A tela de login será aberta imediatamente. Toque no botão de Login do Google.
  3. Você será direcionado para a tela de mensagens se tudo tiver funcionado bem.

6. Ativar o Realtime Database

2efe6805ef369641.png

Importar mensagens

No projeto do Console do Firebase, selecione o item Database na barra de navegação à esquerda. No menu flutuante do banco de dados, selecione Importar o JSON. Procure o arquivo initial_messages.json no diretório do Friendlychat, selecione-o e clique no botão Importar. Isso substituirá todos os dados atualmente no seu banco de dados. Também é possível editar o banco de dados diretamente, usando o + e o x vermelho para adicionar e remover itens.

20ccf4856b715b4c.png

Depois de importar o banco de dados, deve ficar assim:

f3e0367f1c9cd187.png

Confirmar 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

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

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

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

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

FCViewController.swift (em inglês)

  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
  }

Testar a sincronização de mensagens

  1. Clique no botão 98205811bbed9d74.pngRun.
  2. Clique no botão Fazer login para começar para acessar a janela de mensagens.
  3. Adicione novas mensagens diretamente no Console do Firebase clicando no símbolo + verde ao lado da entrada "messages" 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. Substitua pelo código definido abaixo:

FCViewController.swift (em inglês)

  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.pngRun.
  2. Clique em Fazer login para acessar a janela de mensagens.
  3. Digite uma mensagem e pressione "Enviar". A nova mensagem deve estar visível na IU do aplicativo 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'

Ativar o Firebase Storage no painel

Acesse o Console do Firebase e confirme se o Storage está ativado com o domínio "gs://PROJECTID.appspot.com"

b0438b37a588bcee.png

Se, em vez disso, você estiver vendo a janela de ativação, clique em "COMEÇAR" para ativá-la com as regras padrão.

c290bbebff2cafa7.png

Configurar o FirebaseStorage

FCViewController.swift (em inglês)

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

Receber imagens em mensagens existentes

Adicionar código que faz o download de imagens do Firebase Storage.

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

FCViewController.swift (em inglês)

  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 armazenar e enviar 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 dentro da mensagem.

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

FCViewController.swift (em inglês)

  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 de imagem

  1. Clique no botão 98205811bbed9d74.pngRun.
  2. Clique em Fazer login para acessar a janela de mensagens.
  3. Clique no ícone "Adicionar uma foto" para escolher uma foto. A nova mensagem com a foto deve estar visível na interface do aplicativo e no Console do Firebase.

10. Parabéns!

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

O que vimos

  • Realtime Database
  • Login federado
  • Armazenamento

Saiba mais