Codelab Swift de Firebase para iOS

1. Descripción general

2efe6805ef369641.png

Te damos la bienvenida al codelab del chat amigable. En este codelab, aprenderás a usar la plataforma de Firebase con el objetivo de crear aplicaciones para iOS. Implementarás un cliente de chat y supervisarás su rendimiento con Firebase.

Qué aprenderás

  • Permitir que los usuarios accedan.
  • Sincronizar datos con Firebase Realtime Database
  • Almacenar archivos binarios en Firebase Storage

Requisitos

  • Xcode
  • CocoaPods
  • Un dispositivo de prueba con iOS 8.0 o versiones posteriores, o un simulador

¿Cómo usarás este instructivo?

Leer Leer y completar los ejercicios

¿Cómo calificarías tu experiencia con la compilación de apps para iOS?

Principiante Intermedio Avanzado

2. Obtén el código de muestra

Clona el repositorio de GitHub desde la línea de comandos.

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

3. Compila la app de partida

2f4c98d858c453fe.png

Para compilar la app de partida, haz lo siguiente:

  1. En una ventana de la terminal, navega al directorio carpeta_android_studio.pngios-starter/swift-starter desde la descarga del código de muestra.
  2. Ejecución pod install --repo-update
  3. Para abrir el proyecto en Xcode, abre el archivo FriendlyChatSwift.xcworkspace.
  4. Haz clic en el botón 98205811bbed9d74.pngRun.

Después de unos segundos, deberías ver la pantalla principal de Friendly Chat. Debería aparecer la IU. Sin embargo, en este punto no podrás acceder, enviar ni recibir mensajes. La app se anulará con una excepción hasta que completes el siguiente paso.

4. Crea un proyecto de Firebase console

Crear proyecto

En Firebase console, selecciona Agregar proyecto.

Llama al proyecto FriendlyChat y, luego, haz clic en Crear proyecto.

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

Conecta tu app para iOS

  1. En la pantalla Descripción general del proyecto nuevo, haz clic en Agregar Firebase a tu app para iOS.
  2. Ingresa el ID del paquete como "com.google.firebase.codelab.FriendlyChatSwift".
  3. Ingresa el ID de App Store como "123456".
  4. Haga clic en Registrar app.

Agrega el archivo GoogleService-Info.plist a tu app

En la segunda pantalla, haz clic en Download GoogleService-Info.plist para descargar un archivo de configuración que contenga todos los metadatos necesarios de Firebase para tu app. Copia ese archivo en tu aplicación y agrégalo al destino FriendlyChatSwift.

Ahora puedes hacer clic en la "x" en la esquina superior derecha de la ventana emergente para cerrarla (omitiendo los pasos 3 y 4), ya que realizarás esos pasos aquí.

19d59efb213ddbdc.png

Importa el módulo de Firebase

Primero, asegúrate de que se haya importado el módulo Firebase.

AppDelegate.swift, FCViewController.swift

import Firebase

Configura Firebase en AppDelegate

Usa el método “configure” en FirebaseApp dentro de la función application:didFinishLaunchingWithOptions para configurar servicios subyacentes de Firebase desde tu archivo .plist.

AppDelegate.swift

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

5. Identifica a los usuarios

Usa reglas de restricción a usuarios autenticados

Ahora, agregaremos una regla para requerir autenticación antes de leer o escribir cualquier mensaje. Para ello, agregamos las siguientes reglas a nuestro objeto de datos de mensajes. En la sección Database de Firebase console, selecciona Realtime Database y, luego, haz clic en la pestaña Reglas. Luego, actualiza las reglas para que se vean de la siguiente manera:

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

Para obtener más información sobre cómo funciona esto (incluida la documentación sobre la variable “auth”), consulta la documentación de seguridad de Firebase.

Configura las APIs de Authentication

Para que tu aplicación pueda acceder a las APIs de Firebase Authentication en nombre de tus usuarios, deberás habilitarla

  1. Navega a Firebase console y selecciona tu proyecto.
  2. Selecciona Autenticación.
  3. Selecciona la pestaña Método de acceso.
  4. Habilita la opción de Google (azul).
  5. Presiona Guardar en el diálogo resultante.

Si más adelante en este codelab aparecen errores con el mensaje "CONFIGURATION_NOT_FOUND", regresa a este paso y vuelve a verificar tu trabajo.

Confirma la dependencia de Firebase Auth

Confirma que las dependencias de Firebase Auth existan en el archivo Podfile.

Podfile

pod 'Firebase/Auth'

Configura el archivo Info.plist para el Acceso con Google.

Deberás agregar un esquema de URL personalizada a tu proyecto de Xcode.

  1. Abre la configuración del proyecto; para ello, haz doble clic en el nombre del proyecto en la vista de árbol a la izquierda. Selecciona tu app en la sección TARGETS, haz clic en la pestaña Info y expande la sección URL Types.
  2. Haz clic en el botón + y agrega un esquema de URL para tu ID de cliente invertido. Para encontrar este valor, abre el archivo de configuración GoogleService-Info.plist y busca la clave REVERSED_CLIENT_ID. Copia el valor de esa clave y pégalo en el recuadro Esquemas de URL en la página de configuración. Deja los demás campos en blanco.
  3. Cuando termines, la configuración debería ser similar a la siguiente (pero con los valores específicos de tu aplicación):

1b54d5bd2f4f1448.png

Configura el ID de cliente para el Acceso con Google

Después de configurar Firebase, podemos usar el ID de cliente para configurar el Acceso con Google dentro del 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
}

Agrega el controlador de acceso

Una vez que el resultado del Acceso con Google se haya completado correctamente, usa la cuenta para autenticar con 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
      }
    }
  }

Permite que el usuario acceda automáticamente. Luego, agrega un objeto de escucha a Firebase Auth, para permitir que el usuario acceda a la app, después de un acceso exitoso. Quitamos el objeto de escucha en deinit.

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

Salir

Cómo agregar el método Sign out

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

Prueba la lectura de mensajes como un usuario que accedió

  1. Haz clic en el botón 98205811bbed9d74.pngRun.
  2. Deberías ver la pantalla de acceso de inmediato. Presiona el botón Acceso con Google.
  3. Luego, deberías ser dirigido a la pantalla de mensajería si todo funcionó bien.

6. Activa Realtime Database

2efe6805ef369641.png

Cómo importar mensajes

En tu proyecto de Firebase console, selecciona el elemento Base de datos en la barra de navegación izquierda. En el menú ampliado de la base de datos, selecciona Importar JSON (Import JSON). Navega hasta el archivo initial_messages.json en el directorio friendlychat, selecciónalo y haz clic en el botón Import. Esto reemplazará todos los datos que se encuentran actualmente en tu base de datos. También puedes editar la base de datos directamente con el signo + y la X roja para agregar y quitar elementos.

20ccf4856b715b4c.png

Después de importar la base de datos, esta debería verse de la siguiente manera:

f3e0367f1c9cd187.png

Confirma la dependencia de la base de datos de Firebase

En el bloque de dependencias del archivo Podfile, confirma que se incluya Firebase/Database.

Podfile

pod 'Firebase/Database'

Cómo sincronizar mensajes existentes

Agregar código que sincronice los mensajes recién agregados a la IU de la app

El código que agregues en esta sección hará lo siguiente:

  • Inicializa la base de datos de Firebase y agrega un objeto de escucha para controlar los cambios realizados en la base de datos.
  • Actualiza el DataSnapshot para que se muestren los nuevos mensajes.

Modifica los métodos "deinit", "configureDatabase" y "tableView:cellForRow indexPath:" de tu FCViewController; reemplázalo con el código definido a continuación:

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
  }

Sincronización de mensajes de prueba

  1. Haz clic en el botón 98205811bbed9d74.pngRun.
  2. Haz clic en el botón Acceder para comenzar para ir a la ventana de mensajes.
  3. Para agregar mensajes nuevos directamente en Firebase console, haz clic en el símbolo verde + junto a la entrada “mensajes” y agrega un objeto como el siguiente: f9876ffc8b316b14.png
  4. Confirma que el usuario aparezca en la IU de Friendly-Chat.

7. Enviar mensajes

Implementar Enviar mensaje

Envía valores a la base de datos. Cuando usas el método de envío para agregar datos a Firebase Realtime Database, se agrega un ID automático. Estos IDs que se generan automáticamente son secuenciales, lo que garantiza que los mensajes nuevos se agreguen en el orden correcto.

Modifica el método "sendMessage:" de tu FCViewController: reemplázalo con el código definido a continuación:

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

Prueba el envío de mensajes

  1. Haz clic en el botón 98205811bbed9d74.pngRun.
  2. Haz clic en Acceder para ir a la ventana de mensajes.
  3. Escribe un mensaje y presiona enviar. El mensaje nuevo debería aparecer en la IU de la app y en Firebase console.

8. Almacena y recibe imágenes

Confirma la dependencia de Firebase Storage

En el bloque de dependencias de Podfile, confirma que se incluya Firebase/Storage.

Podfile

pod 'Firebase/Storage'

Activa Firebase Storage en el panel

Ve a Firebase console y confirma que el almacenamiento esté activado con el dominio “gs://PROJECTID.appspot.com”.

b0438b37a588bcee.png

Si, en cambio, ves la ventana de activación, haz clic en "COMENZAR" para activarla con las reglas predeterminadas.

c290bbebff2cafa7.png

Cómo configurar FirebaseStorage

FCViewController.swift

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

Cómo recibir imágenes en mensajes existentes

Agregar código que descargue imágenes desde Firebase Storage.

Modifica el método "tableView: cellForRowAt indexPath:" de tu FCViewController y reemplázalo con el código definido a continuación:

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 mensajes de imagen

Implementación de la tienda y el envío de imágenes

Sube una imagen del usuario y, luego, sincroniza la URL de almacenamiento de esta imagen con la base de datos para que la imagen se envíe dentro del mensaje.

Modifica el método "imageSelectorController: didFinish PickingMediaWithInfo:" de tu FCViewController y reemplázalo con el código definido a continuación:

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

Cómo probar cómo enviar y recibir mensajes de imagen

  1. Haz clic en el botón 98205811bbed9d74.pngRun.
  2. Haz clic en Acceder para ir a la ventana de mensajes.
  3. Haz clic en el ícono de "agregar una foto" para elegir una foto. El nuevo mensaje con la foto debería estar visible en la IU de la app y en Firebase console.

10. ¡Felicitaciones!

Has usado Firebase para compilar fácilmente una aplicación de chat en tiempo real.

Temas abordados

  • Realtime Database
  • Acceso federado
  • Almacenamiento

Más información