Bienvenido al codelab de Friendly Chat. En este laboratorio de código, aprenderá a usar la plataforma Firebase para crear aplicaciones iOS. Implementarás un cliente de chat y monitorearás su desempeño usando Firebase.
Este codelab también está disponible en Objective-C.
Lo que aprenderás
- Permitir que los usuarios inicien sesión.
- Sincroniza datos con Firebase Realtime Database.
- Almacena archivos binarios en Firebase Storage.
Lo que necesitarás
- Xcode
- CocoaPods
- Un dispositivo de prueba con iOS 8.0+ o simulador
¿Cómo usarás este tutorial?
¿Cómo calificaría su experiencia con la creación de aplicaciones iOS?
Clona el repositorio de GitHub desde la línea de comandos.
$ git clone https://github.com/firebase/codelab-friendlychat-ios
Para crear la aplicación de inicio:
- En una ventana de terminal, navegue hasta el
ios-starter/swift-starter
directorio de su descarga de código de muestra - Ejecute
pod install --repo-update
- Abra el archivo FriendlyChatSwift.xcworkspace para abrir el proyecto en Xcode.
- Haga clic en el
Botón Ejecutar .
Debería ver aparecer la pantalla de inicio de Friendly Chat después de unos segundos. Debería aparecer la interfaz de usuario. Sin embargo, en este punto no puede iniciar sesión, enviar ni recibir mensajes. La aplicación se cancelará con una excepción hasta que complete el siguiente paso.
Crear proyecto
Desde la consola de Firebase, seleccione Agregar proyecto .
Llame al proyecto FriendlyChat
, luego haga clic en Crear proyecto .
Conecta tu aplicación iOS
- En la pantalla Descripción general del proyecto de su nuevo proyecto, haga clic en Agregar Firebase a su aplicación iOS .
- Ingrese el ID del paquete, como "
com.google.firebase.codelab.FriendlyChatSwift
". - Ingrese la identificación de App Store como "
123456
". - Haga clic en Registrar aplicación .
Agregue el archivo GoogleService-Info.plist a su aplicación
En la segunda pantalla, haga clic en Descargar GoogleService-Info.plist para descargar un archivo de configuración que contiene todos los metadatos de Firebase necesarios para su aplicación. Copie ese archivo a su aplicación y agréguelo al destino FriendlyChatSwift .
Ahora puede 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á esos pasos aquí.
Importar módulo de Firebase
Comience asegurándose de que se importe el módulo de Firebase
.
AppDelegate.swift , FCViewController.swift
import Firebase
Configurar Firebase en AppDelegate
Utilice el método "configure" en FirebaseApp dentro de la aplicación: función didFinishLaunchingWithOptions para configurar los servicios de Firebase subyacentes desde su archivo .plist.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().delegate = self
return true
}
Usar reglas para restringir a usuarios autenticados
Ahora agregaremos una regla para requerir autenticación antes de leer o escribir cualquier mensaje. Para hacer esto, agregamos las siguientes reglas a nuestro objeto de datos de mensajes. Desde la sección Base de datos de la consola de Firebase, seleccione Realtime Database, luego haga clic en la pestaña Reglas. Luego actualice las reglas para que se vean así:
{
"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"), consulte la documentación de seguridad de Firebase.
Configurar API de autenticación
Antes de que su aplicación pueda acceder a las API de Firebase Authentication en nombre de sus usuarios, deberá habilitarla
- Navegue a la consola de Firebase y seleccione su proyecto
- Seleccionar autenticación
- Seleccione la pestaña Método de inicio de sesión
- Mueva el interruptor de Google a habilitado (azul)
- Presione Guardar en el cuadro de diálogo resultante
Si obtiene errores más adelante en este laboratorio de código con el mensaje "CONFIGURATION_NOT_FOUND", vuelva a este paso y vuelva a verificar su trabajo.
Confirma la dependencia de Firebase Auth
Confirma que existan dependencias de Firebase Auth en el archivo Podfile
.
Podfile
pod 'Firebase/Auth'
Configure su Info.plist para el inicio de sesión de Google.
Deberá agregar un esquema de URL personalizado a su proyecto XCode.
- Abra la configuración de su proyecto: haga doble clic en el nombre del proyecto en la vista de árbol de la izquierda. Seleccione su aplicación en la sección OBJETIVOS, luego seleccione la pestaña Información y expanda la sección Tipos de URL.
- Haga clic en el botón + y agregue un esquema de URL para su ID de cliente invertido. Para encontrar este valor, abra el archivo de configuración GoogleService-Info.plist y busque la clave REVERSED_CLIENT_ID. Copie el valor de esa clave y péguelo en el cuadro Esquemas de URL en la página de configuración. Deje los otros campos en blanco.
- Cuando se complete, su configuración debería verse similar a la siguiente (pero con los valores específicos de su aplicación):
Establecer clientID para el inicio de sesión de Google
Después de configurar Firebase, podemos usar el ID de cliente para configurar el inicio de sesión de 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
}
Agregar el controlador de inicio de sesión
Una vez que el resultado del inicio de sesión de Google fue exitoso, use la cuenta para autenticarse 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
}
}
}
Iniciar sesión automáticamente el usuario. Luego agregue un oyente a Firebase Auth, para permitir que el usuario ingrese a la aplicación, después de iniciar sesión correctamente. Y elimine el oyente 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)
}
}
Desconectar
Agregue el método Cerrar 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)")
}
}
Prueba de lectura de mensajes como usuario que inició sesión
- Haga clic en el
Botón Ejecutar .
- Debería ser enviado inmediatamente a la pantalla de inicio de sesión. Toque el botón Iniciar sesión con Google.
- Luego, debería ser enviado a la pantalla de mensajería si todo funcionó bien.
Importar mensajes
En su proyecto en la consola de Firebase, seleccione el elemento Base de datos en la barra de navegación izquierda. En el menú adicional de la base de datos, seleccione Importar JSON . Busque el archivo initial_messages.json
en el directorio friendlychat, selecciónelo y luego haga clic en el botón Importar . Esto reemplazará cualquier dato actualmente en su base de datos. También puede editar la base de datos directamente, usando el verde + y la x roja para agregar y eliminar elementos.
Después de importar, su base de datos debería verse así:
Confirmar la dependencia de la base de datos de Firebase
En el bloque de dependencias del archivo Podfile
, confirme que Firebase/Database
esté incluido.
Podfile
pod 'Firebase/Database'
Sincronizar mensajes existentes
Agrega código que sincronice los mensajes recién agregados a la interfaz de usuario de la aplicación.
El código que agregue en esta sección:
- Inicialice la base de datos de Firebase y agregue un detector para manejar los cambios realizados en la base de datos.
- Actualice
DataSnapshot
para que se muestren nuevos mensajes.
Modifique los métodos "deinit", "configureDatabase" y "tableView: cellForRow indexPath:" de su FCViewController; reemplácelo 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
}
Prueba de sincronización de mensajes
- Haga clic en el
Botón Ejecutar .
- Haga clic en el botón Iniciar sesión para comenzar para ir a la ventana de mensajes.
- Agrega nuevos mensajes directamente en la consola de Firebase haciendo clic en el símbolo verde + junto a la entrada "mensajes" y agregando un objeto como el siguiente:
- Confirme que aparecen en la interfaz de usuario de Friendly-Chat.
Implementar Enviar mensaje
Envíe valores a la base de datos. Cuando use el método push para agregar datos a Firebase Realtime Database, se agregará un ID automático. Estos ID generados automáticamente son secuenciales, lo que garantiza que los mensajes nuevos se agregarán en el orden correcto.
Modifique el método "sendMessage:" de su FCViewController; reemplácelo 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 de envío de mensajes
- Haga clic en el
Botón Ejecutar .
- Haga clic en Iniciar sesión para ir a la ventana de mensajes.
- Escribe un mensaje y presiona enviar. El nuevo mensaje debería estar visible en la interfaz de usuario de la aplicación y en Firebase console.
Confirmar la dependencia de almacenamiento de Firebase
En el bloque de dependencias del Podfile
, confirme que Firebase/Storage
esté incluido.
Podfile
pod 'Firebase/Storage'
Activar Firebase Storage en el panel
Vaya a la consola de Firebase y confirme que Storage esté activado con el dominio "gs: //PROJECTID.appspot.com"
Si, en cambio, está viendo la ventana de activación, haga clic en "COMENZAR" para activarla con las reglas predeterminadas.
Configurar FirebaseStorage
FCViewController.swift
func configureStorage() {
storageRef = Storage.storage().reference()
}
Reciba imágenes en mensajes existentes
Agrega código que descargue imágenes de Firebase Storage.
Modifique el método "tableView: cellForRowAt indexPath:" de su FCViewController; reemplácelo 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
}
Implementar almacenar y enviar imágenes
Cargue una imagen del usuario, luego sincronice la URL de almacenamiento de esta imagen con la base de datos para que esta imagen se envíe dentro del mensaje.
Modifique el método "imagePickerController: didFinishPickingMediaWithInfo:" de su FCViewController; reemplácelo 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])
}
}
}
Prueba de envío y recepción de mensajes de imagen
- Haga clic en el
Botón Ejecutar .
- Haga clic en Iniciar sesión para ir a la ventana de mensajes.
- Haga clic en el icono "agregar una foto" para elegir una foto. El nuevo mensaje con la foto debería estar visible en la interfaz de usuario de la aplicación y en Firebase console.
Has utilizado Firebase para crear fácilmente una aplicación de chat en tiempo real.
Lo que hemos cubierto
- Base de datos en tiempo real
- Inicio de sesión federado
- Almacenamiento