1. Descripción general
Te damos la bienvenida al codelab de Friendly Chat. En este codelab, aprenderás a usar la plataforma de Firebase para crear aplicaciones para iOS. Implementarás un cliente de chat y supervisarás su rendimiento con Firebase.
Qué aprenderás
- Permite que los usuarios accedan.
- Sincroniza datos con Firebase Realtime Database.
- Almacena 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?
¿Cómo calificarías tu experiencia con la compilación de apps para iOS?
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
Para compilar la app inicial, sigue estos pasos:
- En una ventana de la terminal, navega al directorio
ios-starter/swift-starter
de la descarga del código de muestra. - Ejecución
pod install --repo-update
- Abre el archivo FriendlyChatSwift.xcworkspace para abrir el proyecto en Xcode.
- Haz clic en el botón
Ejecutar.
Después de unos segundos, debería aparecer la pantalla principal de Friendly Chat. Debería aparecer la IU. Sin embargo, en este punto no puedes acceder, enviar ni recibir mensajes. La app se anulará con una excepción hasta que completes el siguiente paso.
4. Configura un proyecto de Firebase:
Crea un proyecto nuevo de Firebase
- Accede a la consola de Firebase con tu Cuenta de Google.
- Haz clic en el botón para crear un proyecto nuevo y, luego, ingresa un nombre (por ejemplo,
FriendlyChat
).
- Haz clic en Continuar.
- Si se te solicita, revisa y acepta las Condiciones de Firebase y, luego, haz clic en Continuar.
- (Opcional) Habilita la asistencia de IA en Firebase console (llamada "Gemini en Firebase").
- Para este codelab, no necesitas Google Analytics, por lo que debes desactivar la opción de Google Analytics.
- Haz clic en Crear proyecto, espera a que se aprovisione y, luego, haz clic en Continuar.
Actualiza tu plan de precios de Firebase
Para usar Cloud Storage para Firebase, tu proyecto de Firebase debe tener el plan de precios de pago por uso (Blaze), lo que significa que está vinculado a una cuenta de Facturación de Cloud.
- Una cuenta de facturación de Cloud requiere una forma de pago, como una tarjeta de crédito.
- Si es la primera vez que usas Firebase y Google Cloud, verifica si cumples con los requisitos para obtener un crédito de USD 300 y una cuenta de Facturación de Cloud de prueba gratuita.
- Si realizas este codelab como parte de un evento, pregúntale al organizador si hay créditos de Cloud disponibles.
Para actualizar tu proyecto al plan Blaze, sigue estos pasos:
- En Firebase console, selecciona la opción para actualizar tu plan.
- Selecciona el plan Blaze. Sigue las instrucciones en pantalla para vincular una cuenta de Facturación de Cloud a tu proyecto.
Si necesitas crear una cuenta de Facturación de Cloud como parte de esta actualización, es posible que debas volver al flujo de actualización en Firebase console para completar la actualización.
Conecta tu app para iOS
- En la pantalla de descripción general del proyecto nuevo, haz clic en Agregar Firebase a tu app para iOS.
- Ingresa el ID del paquete como "
com.google.firebase.codelab.FriendlyChatSwift
". - Ingresa el ID de App Store como "
123456
". - Haga clic en Registrar app.
Agrega el archivo GoogleService-Info.plist a tu app
En la segunda pantalla, haz clic en Descargar GoogleService-Info.plist para descargar un archivo de configuración que contiene 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" de la esquina superior derecha de la ventana emergente para cerrarla (omite los pasos 3 y 4), ya que los realizarás aquí.
Importar el módulo de Firebase
Comienza por asegurarte 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 los 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 para restringir el acceso a usuarios autenticados
Ahora agregaremos una regla para requerir autenticación antes de leer o escribir mensajes. 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 Rules. 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 (incluida la documentación sobre la variable "auth"), consulta la documentación de seguridad de Firebase.
Configura las APIs de autenticación
Antes de que tu aplicación pueda acceder a las APIs de Firebase Authentication en nombre de tus usuarios, deberás habilitarla.
- Navega a Firebase console y selecciona tu proyecto.
- Selecciona Autenticación.
- Selecciona la pestaña Método de acceso.
- Mueve el interruptor de Google a la posición de habilitado (azul).
- Presiona Guardar en el cuadro de diálogo resultante.
Si más adelante en este codelab recibes errores con el mensaje "CONFIGURATION_NOT_FOUND", vuelve a este paso y verifica 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 tu archivo Info.plist para el Acceso con Google.
Deberás agregar un esquema de URL personalizado a tu proyecto de Xcode.
- 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.
- 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 URL Schemes de la página de configuración. Deja los demás campos en blanco.
- Cuando termines, la configuración debería ser similar a la que se muestra a continuación (pero con los valores específicos de tu aplicación):
Cómo configurar clientID para Acceder con Google
Después de configurar Firebase, podemos usar el clientID 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 sea exitoso, 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
}
}
}
Accede automáticamente al usuario. Luego, agrega un objeto de escucha a Firebase Auth para permitir que el usuario ingrese a la app después de acceder correctamente. Además, quita 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
Agrega el método de cierre de sesión
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)")
}
}
Probar la lectura de mensajes como usuario con acceso
- Haz clic en el botón
Ejecutar.
- Se te redireccionará de inmediato a la pantalla de acceso. Presiona el botón de acceso con Google.
- Luego, deberías dirigirte a la pantalla de mensajería si todo funcionó bien.
6. Activa Realtime Database
Importar mensajes
En tu proyecto de Firebase console, selecciona el elemento Database en la barra de navegación de la izquierda. En el menú de desbordamiento de la base de datos, selecciona Importar JSON. Navega hasta el archivo initial_messages.json
en el directorio de friendlychat, selecciónalo y, luego, haz clic en el botón Importar. Esto reemplazará todos los datos que se encuentren actualmente en tu base de datos. También puedes editar la base de datos directamente con el signo más verde y la X roja para agregar y quitar elementos.
Después de importar tu base de datos, debería verse de la siguiente manera:
Confirmar la dependencia de Firebase Database
En el bloque de dependencias del archivo Podfile
, confirma que se incluya Firebase/Database
.
Podfile
pod 'Firebase/Database'
Sincronizar mensajes existentes
Agrega código que sincronice los mensajes recién agregados con 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
DataSnapshot
para que se muestren los mensajes nuevos.
Modifica los métodos "deinit", "configureDatabase" y "tableView:cellForRow indexPath:" de tu FCViewController y reemplázalos por el código que se define 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
}
Prueba la sincronización de mensajes
- Haz clic en el botón
Ejecutar.
- Haz clic en el botón Acceder para comenzar y ve a la ventana de mensajes.
- Haz clic en el símbolo + verde junto a la entrada "messages" y agrega un objeto como el siguiente:
.
- Confirma que aparezcan en la IU de Friendly Chat.
7. Enviar mensajes
Implementa Send Message
Envía valores a la base de datos. Cuando usas el método push para agregar datos a Firebase Realtime Database, se agregará un ID automático. Estos IDs generados 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 y reemplázalo por el código que se define 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)
}
Cómo probar el envío de mensajes
- Haz clic en el botón
Ejecutar.
- Haz clic en Acceder para ir a la ventana de mensajes.
- 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
Confirm Firebase Storage Dependency
En el bloque de dependencias de Podfile
, confirma que se incluya Firebase/Storage
.
Podfile
pod 'Firebase/Storage'
Configura Cloud Storage para Firebase
Sigue estos pasos para configurar Cloud Storage para Firebase en tu proyecto de Firebase:
- En el panel izquierdo de Firebase console, expande Compilación y, luego, selecciona Storage.
- Haz clic en Comenzar.
- Selecciona una ubicación para tu bucket de Storage predeterminado.
Los buckets enUS-WEST1
,US-CENTRAL1
yUS-EAST1
pueden aprovechar el nivel “Siempre gratuito” para Google Cloud Storage. Los buckets de todas las demás ubicaciones siguen los precios y el uso de Google Cloud Storage. - Haz clic en Comenzar en modo de prueba. Lee la renuncia de responsabilidad sobre las reglas de seguridad.
Más adelante en este codelab, agregarás reglas de seguridad para proteger tus datos. No distribuyas ni expongas una app de forma pública sin agregar reglas de seguridad para tu bucket de Storage. - Haz clic en Crear.
Configura FirebaseStorage
FCViewController.swift
func configureStorage() {
storageRef = Storage.storage().reference()
}
Cómo recibir imágenes en mensajes existentes
Agrega el código que descarga imágenes de Firebase Storage.
Modifica el método "tableView: cellForRowAt indexPath:" de tu FCViewController y reemplázalo por el código que se define 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. Envía mensajes con imágenes
Implementa la función Store and Send Images
Sube una imagen del usuario y, luego, sincroniza la URL de almacenamiento de esta imagen con la base de datos para que se envíe dentro del mensaje.
Modifica el método "imagePickerController: didFinishPickingMediaWithInfo:" de tu FCViewController y reemplázalo por el código que se define 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 el envío y la recepción de mensajes con imágenes
- Haz clic en el botón
Ejecutar.
- Haz clic en Acceder para ir a la ventana de mensajes.
- Haz clic en el ícono "Agregar una foto" para elegir una foto. El mensaje nuevo con la foto debería ser visible en la IU de la app y en Firebase console.
10. ¡Felicitaciones!
Usaste Firebase para compilar fácilmente una aplicación de chat en tiempo real.
Temas abordados
- Realtime Database
- Acceso federado
- Almacenamiento