Aprende a usar la Arquitectura Model-View-Controller con Coordinators en Swift
Al usar arquitecturas podemos aplicar diferentes patrones. En este ejemplo vamos a usar el Patrón Coordinator dentro de la arquitectura Model View Controller en Swift. Este patrón nos permite encapsular la lógica de navegación dentro de una clase que solo tiene esta responsabilidad
Tabla de contenido
Hoy en SwiftBeta vamos a aprender a añadir Coordinators dentro de la Arquitectura Model-View-Controller. Lo que vimos en el anterior video fue la arquitectura Model-View-Controller, donde creábamos una app desde cero. Esta app realizaba una petición HTTP a la API de rick and morty, y también realizaba una navegación al pulsar uno de los personajes en la lista. Esta nueva vista mostraba información de detalle del personaje seleccionado. Todo esto lo hicimos siguiendo la arquitectura Model-View-Controller, una arquitectura muy usada dentro del mundo iOS.
Al finalizar el anterior video, navegábamos de un ViewController a otro, y esta lógica de navegación la teníamos dentro del ViewController inicial (Esta que os estoy mostrando ahora mismo). Sabíamos exactamente qué celda del UITableView se había pulsado, y en base a esa información recuperábamos el modelo y se lo pasábamos al ViewController que se iba a mostrar. Pues bien, poara poder desacoplar esta lógica vamos a usar un patrón muy usado en aplicaciones móviles llamado Coordinators. De esta manera crearemos un componente nuevo que sabrá como manejar la navegación dentro de nuestra app, quitándole esta responsabilidad al ViewController.
Esto tiene muchas ventajas, desde el desacople de lógica, poder reutilizar navegaciones en otras partes de tu app, crear multiples navegaciones push o modales, etc.
Coordinators
Vamos a continuar con nuestro proyecto, el que vimos en el anterior video.
Los coordinators son muy sencillos de implementar, y hoy vamos a ver un ejemplo muy básico pero potente. Lo que vamos hacer es crear una carpeta llamada Coordinators, y dentro de ella vamos a crear un fichero nuevo que tendrá el siguiente protocolo:
Lo único que he hecho ha sido crear un protocolo llamado Coordinator. Este protocolo tiene dos variables opcionales llamadas viewController y navigationController. Estas variables nos servirán para hacer un present en el caso de querer presentar de forma modal un ViewController. O un push en el caso de presentar un ViewController cuando usamos un NavigationController.
También, hay un método llamado start que es el que lanzará la acción de navegar. Todo esto lo vamos a implementar ahora en un MainCoordinator.
Para finalizar, he añadido un valor por defecto de nil a las variables del protocolo Coordinator.
MainCoordinator
Lo que vamos a crear ahora es un MainCoordinator. Es muy interesante por que lo vamos a llamar cuando se ejecute nuestra app.
Al crear nuestro MainCoordinator, hacemos que conforme nuestro protocolo Coordinator. En este caso vamos a inicializarlo con un NavigationController que le inyectaremos cuando lo inicialicemos desde el SceneDelegate en los siguientes minutos.
Para acabar de conformar el protocolo Coordinator, necesitamos implementar el método start(), pues vamos a ello:
Al cargar nuestro ViewController desde el Storyboard (Como vimos en el video sobre Storyboards y XIBs), necesitamos instanciar CharactersListViewController de esa manera.
Una vez tenemos esta parte listada, tenemos que ir a nuestro punto de entrada de la app:
Ahora ya estamos listos para utilizar nuestro MainCoordinator, si compilamos vamos a ver qué ocurre. Obtenemos un crash, eso es porque nos hemos dejado un paso, hay que añadir el identificador del view controller en el Storyboard, de esta manera podemos instanciar correctamente el ViewController CharactersListViewController (esto también lo vimos en el video de Storyboards)
Una vez añadido el Storyboard ID, vamos a volver a compilar.
Perfecto, hemos creado nuestro primer Coordinator.
PushCoordinator
Ahora vamos a añadir otro ViewController, vamos a crear una nueva clase llamada CharacterDetailPushCoordinator
Esta clase tiene lo necesario para realizar la navegación que estábamos haciendo para poder navegar al CharacterDetail. Vamos a crear una instancia de esta nueva clase en nuestro ViewController:
Creamos la propiedad:
Y ahora sustituimos el código de navegación que estaba directamente en el ViewController:
Si compilamos vemos que funciona perfectamente, y que hemos movido la responsabilidad de la navegación a otra clase. A otra clase que su única responsabilidad es esa, la navegación.
ModalCoordinator
Imagina si es potente, que si ahora queremos presentar modalmente el CharacterDetail, podemos crear el siguiente Coordinator, lo vamos a llamar CharacterDetailModalCoordinator
Y lo único que tendríamos que hacer en nuestro ViewController es crear la propiedad:
Y cambiar el PushCoordinator que teníamos, por el nuevo que acabamos de crear:
No hemos tenido que modificar lógica en nuestro ViewController, ya que hemos creado una ModalCoordinator que se encarga de presentar el ViewController.
Si compilamos, vemos que ahora al pulsar una celda, se presenta modalmente el CharacterDetailViewController.
Inyectar dependencias en el CharactersListViewController
Quizás es pronto para hablar de esto, pero voy a empezar a introducirlo. A mi me encanta sacar responsabilidades en clases, y que estas clases conformen un protocolo, una interfaz o también lo puedes llamar contrato. De esta manera, al crear los tests es muy fácil mockear (ojo que esto es un término nuevo que aún no había sacado en el canal).
En el caso de la vistas, están en una clase aparte y podríamos hacer snapshots tests directamente usando CharactersListView o CharacterDetailView (esto lo veremos en próximos videos).
En CharactersListViewController podríamos dejar de usar el Storyboard, e instanciar el ViewController con un init todas sus propiedades, de esta manera podríamos inyectar mocks para nuestros tests. Pero no te preocupes, que esto lo veremos en otros videos.
Conclusión
Hoy hemos aprendido a cómo usar el Patrón Coordinator dentro de la arquitectura Model-View-Controller. Este Patrón nos permite extraer la responsabilidad de navegación que añadiamos en nuestro ViewController para navegar a otro. Esta responsabilidad está encapsulada dentro de un Coordinator que se encarga de navegar a otras pantallas.