desarrollo ios realtime database google firebase

Google Firebase en iOS: La realtime database

Una de las principales funcionalidades de las apps móviles hoy en día es la de poder mostrar datos personalizados al usuario: mis preferencias, mis favoritos, mis posts, mis fotos, etc.

Además, es comprensible que nuestros usuarios quieran tener sus datos sincronizados entre todos sus dispositivos, ya sean iOS o Android.

Para ello utilizamos las bases de datos en los servicios de backend de nuestras apps. Estas bases de datos pueden ser de diferentes tipos y marcas (hay cientos de ellas), pero el objetivo final de todas ellas es guardar de forma segura los datos de nuestros usuarios, así como mantenerlos sincronizados.

Hoy te vengo a hablar de la realtime database (RTDB) de Firebase: una base de datos “No SQL”. En la RTDB se estructura la información en forma de árbol, siguiendo un esquema similar a JSON, pero no exactamente igual, ya que no se soportan arrays, sino únicamente pares clave-valor. Te lo cuento más abajo.

Tipos de datos soportados

Antes te decía que la estructura de la realtime database nos recuerda a un fichero JSON, con algunas excepciones. Y es que lo primero que me sorprendió cuando usé la RTDB es que los arrays no están soportados y tienes que hacer algún malabar para simularlos. Lo que se suele hacer es tener un nodo padre con el nombre del array, que tenga tantos nodos hijos como valores del array, donde cada hijo es un mapa clave valor, con clave igual al valor del array, y valor igual a “1” por ejemplo. Parece complicado, pero te acabas acostumbrando:

desarrollo ios google firebase array realtime database

Representación de una array en la realtme database de firebase

En el ejemplo de arriba, tenemos a un usuario con su listado de chats. Cada chat es un [chatID: timestamp], de forma que puedo saber el momento en el que se unió al citado chat.

El resto de tipos de datos soportados son los mismos que podemos ver en un JSON habitualmente: strings, numéricos, y booleanos.

 

Modo offline

Una cosa super guapa de la realtime database es su modo offline. Y es que está implementado el caso de uso “offline first” por defecto. Es decir, cuando hacemos una query a la RTDB para obtener mi listado de chats, primero se me devuelven los datos locales, y después, si hay cambios, los remotos. De esta forma no tenemos al usuario esperando durante la fetch inicial.

Si nos quedamos sin conexión momentáneamente y escribimos en la base de datos, la operación será guardada y enviada posteriormente cuando la conexión vuelva a estar activa. Todo esto sin tener que hacer nada como usuarios del API de la RTDB, ya que firebase gestiona los conflictos y hace el trabajo pesado por nosotros. Mola, ¿no?

 

Recuperando datos de la realtime database

Las queries se ejecutan de una manera especial. Y es que lo que hacemos no es exactamente recuperar datos discretos, sino más bien escuchar a cambios que suceden en los nodos que nos interesan.

Subscripción a eventos

Los tipos de cambios a los que podemos suscribirnos sobre un nodo son: childAdded, childRemoved, childChanged, childMoved. Los nombres de estos eventos son autoexplicativos, así que no quiero entrar mucho en detalle. Pero te puedes imaginar que firebase nos avisará cuando bajo determinado nodo se añadan, eliminen, cambien, o muevan alguno de sus hijos.

Lo guay de todo esto es que firebase nos avisa en tiempo real, de forma que podemos actualizar nuestra UI de manera casi instantánea. Imaginaros un nodo de mensajes enviados a un chat. Cuando abro el chat en mi app, pondré un observer bajo el nodo “ChatMessage/{chatID}”, de forma que cada vez que alguien envía un mensaje al chat, lo podré pintar en mi interfaz.

También podemos escuchar a un único cambio. Es decir, en lugar de configurar un observer que nos avisa de todos los cambios hasta que dejemos de observarlos, podemos establecer un observer para escuchar un único evento, y ninguno más. Esto puede servirnos para hacer una query de un valor discreto, de forma similar a lo que haríamos con una query “tradicional”.

A parte de los eventos mencionados a los que podemos suscribirnos, también podemos escuchar a un evento “value”. Este evento nos avisa de cualquier cambio que ocurra no sólo en la ruta que establezcamos, sino también en los nodos secundarios/hijos. Por ello hay que tener cuidado y no configurarlo para el nodo raíz de nuestra base de datos, porque estaríamos escuchando a todos los cambios.

Queries y filtrado

Firebase RTDB nos ofrece 3 tipos de ordenación, y 5 tipos de filtros, que podemos combinar como queramos.

Si queremos ordendar, podemos hacerlo usando queryOrderedByKey, queryOrderedByValue, o queryOrderedByChild. Son métodos autoexplicativos así que no me extenderé: podemos ordenar la query por clave, por valor, o por el valor de algún atributo concreto. Por ejemplo, imagina que queremos recuperar los posts de un usuario con más likes en una app tipo red social:

Una gran limitación de las queries de la RTDB es que no podemos hacer queries de nodos sobre valores que están más profundos en la jerarquía de dicho nodo, cosa que se resuelve con la hermana mayor de la RTDB, Firestore, de la cual te hablaré en futuros posts.

 

También podemos limitar el los resultado con las opciones de filtrado: queryLimitedToFirst, queryLimitedToLast, queryStartingAtValue, queryEndingAtValue y queryEqualToValue. Una vez más, los nombres explican los filtros por sí mismos: podemos limitar los resultados al último, el primero, y a los que empiecen, finalicen o sean igual a determinado valor.

 

Escribiendo datos en la realtime database

Escribir datos en la RTDB es super trivial. Simplemente elige el nodo en el que quieres guardar tu información, y llama a “setValue”. Pero OJO, si usas setValue de esta forma, sobreescribirás todos los datos en esa ruta, incluyendo los nodos secundarios. Por ejmplo:

sobreescribirá a nul todos los atributos de user, excepto el username, que lo pondrá a “Joseba”.

Si quieres actualizar un dato sin sobreescribir el resto, puedes hacer dos cosas: bien actualizar únicamente ese atributo username usando el path “User/{userID}/username”:

o bien usar updateChildValues sobre el path “User/{userID}”:

De esta forma, sólo actualizarás el dato que te interesa, dejando intacto el resto.

 

Transacciones en la realtime database

Hay ocasiones en las que tenemos que asegurar que operaciones de lectura y escritura se efectúan de forma atómica sin que ningún otro acceso a la base de datos nos afecte. Un ejemplo típico es incrementar un valor. Para hacerlo, primero hay que leer el valor, actualizarlo en memoria y volver a escribirlo. No podemos permitir ninguna escritura del valor durante este proceso, o el dato que esribiremos estará basado en un valor desactualizado.

Para ello la RTDB nos ofrece las transacciones. Son operaciones que están garantizadas que se realizarán de forma atómica. El patrón es similar al de otras operaciones: seleccionar el path donde está el nodo que queremos actualizar, llamar a “runTransactionBlock”, que nos devolverá el objeto en memoria para que lo manipulemos como queramos, y finalmente llamar un método de completado para avisar a firebase de que ya hemos terminado.

Si la transacción falla porque hubo alguna escritura mientras manipulábamos los datos en memoria, firebase repite la operación por nosotros hasta que sea satisfactoria. Así. Sin más. ZERO DRAMAS.

 

Gestionando la presencia

En muchas apps es requisito mostrar la presencia online/offline de los usuarios del sistema, por ejemplo, en apps tipo chat.

La gestión de la presencia puede ser muy engorrosa si la implementamos manualmente. Por ejemplo, en el caso de iOS, cuando damos al botón home y movemos la app a background, es el sistema operativo el que, en función de los recursos disponibles, toma la decisión de matar nuestra app. Y en ese caso no tenemos margen de maniobra.

Firebase mantiene nuestra presencia actualizada en la RTDB comprobando la conectividad costantemente, de forma que se entera de cuando un cliente está inactivo

 

Además nos ofrece un conjunto de métodos “onDisconnect” para que ejecutemos operaciones sobre un nodo cuando se detecta que estamos inactivos. Esta operación se ejecuta en servidor, precisamente por lo que te comentaba más arriba: para cuando la app está cerrada por el sistema (o por el propio usuario), ya no tenemos margen de maniobra.

 

Reglas de seguridad

Si bien este apartado merece un post a parte, te quiero mencionar por encima las posibilidades que ofrece la RTDB en cuanto a seguridad.

Las reglas de seguridad siguen una sintaxis similar a JavaScript. Es una especie de fichero JSON, escrito en lenguaje con el que podemos decidir por cada nodo de la base de datos, quién y cuándo puede escribir o leer, establecer reglas de validación que se han de cumplir para llevar a cabo la operación, y generar índices en nodos secundarios.

El nodo raíz se llama “rules”, y bajo él cuelgan todas las reglas que queramos añadir a cada nodo.

No me gusta avasallarte con código en el blog (existen muchos blogs, muy buenos y con código excelente), pero sí te quiero poner un ejemplo de qué puedes hacer, dado un determinado nodo “User/{userID}”:

Podemos, por ejemplo, hacer que sólo puedan escribir ese nodo los usuarios autenticados, y que además al escribir un usuario, obligar a que el campo “displayName” sea de tipo String. Para conseguir esto, necesitaríamos esta información en el fichero de reglas:

De esta forma estamos diciendo justamente eso: permite la escritura a todos los autenticados, y valida que displayName exista y que sea de tipo String. En este caso, “auth” y “newData” son variables predefinidas, que se pueden usar junto con las otras disponibles (now, root, data), para establecer reglas y validaciones más complejas.

Como te decía, este apartado da para escribir un post por separado, así que mientras lo preparo, te dejo aquí un enlace a la documentación con varios ejemplos.

 

Conclusión

Guardar datos en un servidor remoto es un requisito casi indispensable en cualquier app que se publica hoy en día. Firebebase realtime database es una herramienta que nos ayuda precisamente a eso: guarda y recupera datos de forma sencilla, implementa por defecto tanto offline first como transacciones, y sin tener que preocuparnos por alojar nada en ningún servidor.

Pero puede que no sea una solución válida para todos los casos. Si quieres hacer queries anidadas, tendrás que encadenar varias peticiones a Firebase. Además, hay que hacer malabares para gestionar coleciones tipo array. Y por último, y no por ello menos grave, no puedes hacer queries en nodos sobre valores profundos en la jerarquía, o dicho de otra forma, sólo puedes hacer queries en un nodo por los valores de sus primeros hijos en la jerarquía.

Si te perdiste los últimos posts sobre Firebase, recuerda que ya te di una visión general, y hablé de analytics, y de autenticación anteriormente.

¿Y tú, qué solución de base de datos implementas en tu app? ¡Cuéntamelo en los comentarios!

 

No hay comentarios

Deja un comentario