Una untadita de VIPER

Una untadita de VIPER

Hola, como lo había comentado en este post, agregué la misma solución al repositorio de ejemplo pero ahora con VIPER, pero antes...

¿Qué es VIPER?

VIPER es un patrón de cinco capas: View, Interactor, Presenter, Entity y Router, seeeeee, yo sé que te estarás preguntando:

¿Y para qué o qué?, si todo lo podría meter en mi UIViewController

y efectivamente, la espalda de MVC está cansada de soportar tantas aplicaciones en la App Store, jajajajajaja (esto último no lo sé), lo que sé es que es el patrón didáctico de UIKit, es super simple de aprender e implementar, hay bastantes soluciones para disminuir el código en los controladores pero, al final, no suele ser fácil de mantener, testear y, sobre todo, escalar para aplicaciones con cierto grado de complejidad.

¿Por qué VIPER?

Hablando de complejidad, VIPER es un arma de dos filos ya que en aplicaciones simples es sencillamente una solución con sobre ingeniería ya que muchas de las capas se convierten en tan sólo proxies que redireccionan llamadas, como el Presenter. Pero en aplicaciones cuya complejidad de la capa de dominio es grande, VIPER nos ayuda a segmentar todas las condiciones dependientes de negocio en el Interactor y los Workers o Data Managers entonces, como acabas de leer, VIPER se usa en aplicaciones GRANDES que requieren mucha escalabilidad y centrar el negocio en capas fáciles de mantener y testear.

Si tu aplicación es sencilla creo que hay soluciones más simples de implementar además de que actualmente gracias a la programación de interfaz por componentes de SwiftUI, el nuevo patrón por default para hacer aplicaciones en iOS (y el resto de los sistemas operativos) es MVVM, una solución más robusta que Massive View Controllers.

¿Cómo funciona VIPER?

Compuesto por cinco capas la comunicación es bidireccional en estas tres View <-> Presenter <-> Interactor y el Interactor -> Data Manager y el Presenter -> Router, la comunicación es a través de protocolos y el actor principal por el que pasa todo el flujo es el Presenter. Los protocolos funcionan como "skeletons" en donde se estructura todo el comportamiento de los actores sin su definición, esta característica nos permite el uso de Test Doubles de una forma satisfactoria, creando fácilmente Mocks, Stubs, Fakes, Dummies y Spies, creo que a pesar de ser un patrón grande que, en un inicio puede parecer complejo, realmente es sencillo de implementar cuando comienzas a tener experiencia con el mismo.

VIPER en el proyecto

Agregarlo sencillo, lo complicado creo que siempre será la descomposición del problema así que comencemos.

Esqueleto inicial

Aquí nos ayudaremos de los protocolos en donde definiremos el comportamiento de nuestras actores, ¿ok?, ¿te queda claro? Veamos lo que hace nuestra nuestra vista de ejemplo:

  1. Tiene un botón que dice TAP que al tocarlo desencadena una acción
  2. La acción crea un saludo, es decir, una cadena que se forma al extraer las propiedades de una estructura.
  3. El saludo es mostrado en una etiqueta en la vista... Simple, ¿verdad?, bueno, eso pasémoslo a protocolos:

    protocol MessageViewProtocol: UIViewController {
     /* Función que captura la acción del botón al ser presionado, en donde solicitaremos el saludo */
     func didTapButton(_ sender: UIButton)
     /* Función en donde nos será regresado el saludo que mostraremos en la etiqueta */
     func show(_ message: String)
    }
    

    Ahora la implementación ya no va aquí, como en un clásico MVC, pues nuestro Presenter debe de tener una función para solicitarle los datos al momento de tocar el botón y otro que nos proporcione dicho saludo para que, a su vez, este se lo pase a la vista a través de su función show(_:)

    protocol MessagePresenterProtocol: AnyObject {
     /* Función en donde se solicita el saludo al Interactor, ejecutada desde la Vista */
     func askMessage()
     /* Función en donde el Interactor nos regresa el saludo que formó a partir del modelo */
     func retrieve(message: String)
    }
    

    Y a su vez, el Presenter le tiene que solicitar la información al Interactor ya que esta capa es la que tiene acceso al modelo o a la persistencia de datos o a la capa de red

    protocol MessageInteractorProtocol: AnyObject {
     /* Función en donde se prepara el saludo con el modelo Person */
     func prepareMessage()
    }
    

    Y ya, es momento de crear el código para cada una de las funciones AKA la implementación ya que los protocolos sólo fueron la definición del comportamiento; de igual forma no pienso entrar en muchos detalles ya que no es el objetivo de este post, es decir, si tienes dudas más puntuales deja un comentario o mándame un tuitazo' hehehehehehe por lo mientras aquí te dejo la implementación de las dos funciones de nuestra vista, super simple, ¿verdad?

    ...
    // MARK: - MessageViewProtocol implementation
    func didTapButton(_ button: UIButton) {
     presenter?.askMessage()
    }
    
    func show(_ message: String) {
     greetingLabel?.text = message
    }
    ...
    

    Además de que aquí puedes ver todo el código.

What's next?

Aquí no estamos viendo una capa de Networking o algún modo de persistencia como CoreData, lo sé; espero que en algún momento pueda implementar, al menos una capa sencilla. Lo único que te puedo decir como recomendación es que esto sería responsabilidad del Interactor, ¿ok? No lo pongas en el Presenter e igual, las navegaciones entre vistas (o controladores) va en el Router... ¡POR MÁS SENCILLA QUE SEA DICHA NAVEGACIÓN!, ¡aunque sea una simple alerta para mostrar un mensaje de error! Toda navegación va en el Router.

🚀