Rearquitectura en iOS para añadir Testing, de MVC a MVVM

La aplicación iOS sobre la que trata esta rearquitectura es SGEmployee. Es un app interna a SolidGear cuya principal función es la solicitud de vacaciones por parte de los empleados. También incluye otras funciones como la visualización de los tweets de la cuenta de Twitter, perfil de usuario, tablón de noticias, sección para ver los demás trabajadores… Aunque la parte más importante es la solicitud de vacaciones: cada empleado puede ver los días de vacaciones disponibles, sus solicitudes de vacaciones y la información de cuáles han sido aprobadas o rechazadas. A continuación tenemos un par de capturas de todas las secciones y de las vacaciones.

Estado inicial

En  primer lugar vamos a hablar del estado en el que se encontraba la aplicación antes de comenzar a trabajar en ella.

  • SGEmployee no había recibido ninguna actualización importante desde Mayo de 2017 (obviando actualizaciones de librerías).
  • La aplicación utilizaba MVC (Modelo Vista Controlador) como patrón de arquitectura de software.
  • Toda la interfaz se encontraba en un mismo storyboard, lo que provocaba que XCode funcionara de forma lenta y dificultaba la corrección de bugs o la necesidad de cambio de alguna de las vistas.
  • Para el acceso a los datos necesarios para la app, se usa el patrón Observador con la ayuda de la clase NotificationCenter.
  • La aplicación era completamente funcional pero se encontraba en un estado de acoplamiento que hacía muy complicada la tarea de incluir tests en varios niveles, por lo que no tenía ningún tipo de test.

Después de conocer el estado de la app, se planteó la posibilidad de hacer una rearquitectura completa de la misma. Esta rearquitectura comprende el cambio del patrón de arquitectura de software a MVVM (Model View ViewModel), el cambio del patrón de acceso a datos a un patrón Repositorio y la inclusión de tests en tres niveles: de interfaz de usuario, unitarios y de red. El objetivo principal de esta rearquitectura es que, a través de esta arquitectura desacoplada basada en los principios SOLID, somos capaces de utilizar la inyección de dependencias para añadir tests. A estos cambios le añadimos la reestructuración de las interfaces, lo que supone separar cada interfaz (o cada parte de ella) en un fichero (xib).

Reestructuración interfaces

Para esta tarea era necesario separar todos los elementos de cada ViewController del Storyboard. Pero cada ViewController no equivale siempre a un fichero ya que muchas veces esa interfaz estaba compuesta por celdas de una tabla, que usando xib implica separar la interfaz de la tabla de la de la celda.

MVVM

Modelo – Vista – Modelo de Vista es un patrón de arquitectura software que como muchos otros, separa la lógica de la aplicación de la lógica de la vista de la aplicación. El Modelo representa la lógica de negocio, debe ser una estructura de código sencilla (class o struct) que define las propiedades de un objeto y no tiene funcionalidad. La Vista no debe tener lógica, sólo se encarga de modificar los elementos de la vista según lo ordene el Modelo de la Vista. El Modelo de la Vista es el intermediario entre el Modelo y la Vista, contiene la lógica de presentación.

Para hacer el cambio de arquitectura de MVC a MVVM, en primer lugar, hay que identificar en el Controlador la lógica que se encarga de modificar los elementos de la vista. Una vez identificada esta lógica, hay que separarla en la clase Modelo de la Vista para que sea esta la que decida cuándo se muestran o no los datos en la interfaz, cuándo se muestra una parte de la misma… En resumen, todos los elementos de la vista que requieran algo de lógica para mostrarse o no en la vista, son responsabilidad de la Vista del Modelo.

Patrón repositorio

Como definición, el patrón repositorio es una fachada que abstrae el dominio de la persistencia, es decir, es una capa intermedia entre la aplicación y la base de datos.

Con este patrón buscamos tener una estructura desacoplada en la que las acciones que se realizan en cada parte estén bien definidas y separadas. Este cuenta con tres partes: repositorio, APIClient y Network que funcionan de la siguiente manera. La explicación se va a hacer de la forma más sencilla para mí, del servidor hacia la aplicación. Pero para hacer la petición al servidor, el Modelo de la Vista tiene que pedir al repositorio los datos que necesita para la vista, éste se los pedirá al APIClient que a su vez se los pide al Network.

  • Network. Usando la librería Alamofire pedimos (o subimos) los datos necesarios al servidor, que devuelve un JSON como respuesta que es “enviado” al APIClient.
  • APIClient. Este apartado se encarga de decodificar la respuesta JSON recibida, en un formato que pueda ser usado por el Repositorio.
  • Repositorio. Este se encarga de guardar los datos recibidos en base de datos (en este caso, ya que puede haber otras apps que no usen base de datos en la propia aplicación), recuperar los datos necesarios de la base de datos y devolver dichos datos a la Vista del Modelo

En la siguiente figura podemos ver un esquema de la estructura de este patrón.

En esta parte había que desacoplar y reestructurar todo el acceso a datos (base de datos y servidor) para hacerlo siguiendo el patrón repositorio. Para hacer este cambio fue necesario, en primer lugar, localizar dónde y cómo se hacían las peticiones al servidor, el posterior guardado en base de datos y la recuperación de los mismos de la base de datos. A continuación era necesario adaptar el código al patrón de la forma que se ha descrito en el párrafo anterior y que siga siendo funcional. En muchas ocasiones esto no era tan sencillo como puede parecer, ya que era podía ser necesario cambiar el código existente porque no era adaptable al nuevo patrón.

UI, Unit and Network tests

Ya hemos llegado al último punto, el objetivo, la razón de todas la partes anteriores. Vamos a dividir los tests en tres partes o tipos:

  • Tests de interfaz de usuario: consiste en mostrar cada interfaz y comprobar que los elementos mostrados son los esperados. Si todos elementos son los correctos, el test pasaría. Esto nos permite comprobar, de manera automática y para diferentes escenarios, que la interfaz es la esperada y, si no es así, poder corregir los errores de forma rápida al saber exactamente dónde se encuentran.
  • Tests unitarios: son los que permiten comprobar que las funcionalidades de la aplicación funcionan como se espera. Estas comprobaciones se realizan de forma aislada, que es posible gracias a la arquitectura desacoplada.
  • Tests de red: este tipo de test permite simular, sin necesidad de conexión a internet o con el servidor, la respuesta del servidor para comprobar que el comportamiento de nuestra API de red es el esperado. El procedimiento es el siguiente: con la librería Hippolyte generamos un stub, o respuesta “falsa” del servidor, que se construye con un código HTTP y un json (simulando la respuesta json del servidor).  Con esta respuesta falsa del servidor comprobamos que la respuesta de nuestra API de red es la que esperamos.

Este es sólo un resumen, el tema de los tests en iOS se trata con más detalle en otra entrada de este blog.

Deja un comentario

Responsable » Solidgear.
Finalidad » Gestionar los comentarios.
Legitimación » Tu consentimiento.
Destinatarios » Los datos que me facilitas estarán ubicados en los servidores SolidgearGroup dentro de la UE.
Derechos » Podrás ejercer tus derechos, entre otros, a acceder, rectificar, limitar y suprimir tus datos.

Envíando este formulario aceptas la política de privacidad.

¿Necesitas una estimación?

Calcula ahora