Coordinators en IOS: Implementación a través del Patrón Estrategia

El patrón Coordinator es usado para desacoplar los ViewControllers de la lógica de navegación entre Vistas es decir, que la responsabilidad de administrar los flujos entre vistas no sea asumida por los ViewControllers. Los Coordinators también ayudan a aplicar Clean Architecture a la solución IOS. Se considera que es un patrón que se presenta en la capa Presentation (Interface Adapters) así como se muestra en la figura siguiente:


Desde que fue presentado por Soroush Khanlou se han realizado diferentes tipos de implementación de este patrón. En esta ocasión propongo una forma de implementación usando el patrón de diseño base conocido como Patrón Estrategia (Strategy Pattern) y usando Swift como lenguaje. Pero antes que nada, recordemos el contexto del Patrón Estrategia.

Patrón Estrategia

A través de este patrón es posible configurar una determinada estrategia de algoritmos para un cliente determinado y con la ayuda de un contexto administrar dichas estrategias.



Implementación del Coordinator

Aplicando el patrón estrategia la implementación del coordinator sería de la siguiente forma:


Coordinator: Es la representación abstracta de la estrategia que en este caso es la de coordinar el flujo de navegación entre vistas. Define una operación abstracta llamada start() delegando su implementación a las subclases.

AppCoordinator, FirstCoordinator, SecondCoordinator: Son las subclases de Coordinator que implementaran la estrategia en cada uno de sus casos. A cada viewController se le podrá asignar un ViewModel y un Coordinator.

Context: Administra y conoce las múltiples estrategias, en este caso se utiliza el aprovechamiento de pasarse a sí mismo a los coordinators concretos a través de dependency injection en constructor.

Cliente: En este caso es representado por los ViewControllers de la aplicación, quienes a través del contexto podrán acceder a la estrategia que necesitan.

Consideraciones de la implementación


  • Este aprovechamiento no considera que sea necesario una jerarquía y dependencia entre coordinators, es decir ningún coordinator concreto es padre o hijo de otro, todos son tratados al mismo nivel. Sin embargo a través del Patrón Composición (Composition Pattern) se podría implementar si fuese necesario.
  • El Contexto puede pasarse a sí mismo a los coordinators no solamente como dependency injection en constructor si no también como método, sin embargo hay que tener en cuenta que para que el coordinator pueda acceder a una referencia del contexto en cualquier momento es mejor tener el contexto como propiedad del coordinator y pasar la dependencia como constructor, de lo contrario estaría bien si pasa la dependencia por método.
  • Lo mismo aplica desde el punto de vista del coordinator concreto que requiera pasarse a sí mismo como argumento al Contexto. 
  • Para garantizar que esta relación de doble vía entre el Contexto y Coordinators no generen referencias fuertes (strong references) que podrían derivar en fugas de memoria se recurre a la estrategia de definir referencias débiles (weak references) que proporcionan Swift.
  • La implementación puede también ser mejorada aplicando el Patrón Template Method en los coordinator concretos en el método start() de tal forma que se pueda configurar varios pasos de configuración básica y configuración extra antes de llamar a la siguiente vista a través del contexto y el método push().
  • Se puede integrar a la implementación el Patrón MVVM que recordemos es otro patrón de diseño que permite aplicar Clean Code en el Front de la aplicación.
  • El Contexto puede también ser usado para envío de datos entre ViewModels.

Evidencias

Se valida la implementación del patrón en una aplicación construida con 5 vistas (screens) y se configura diferentes opciones de navegación para A/B Testing:




Repositorio

El código de la implementación se encuentra ubicada en mi repositorio CoordinatorPatternDemo-IOS.

Conclusiones

La aplicación del Patrón Estrategia para la implementación Coordinators es flexible, limpia y fácil de adoptar. Se puede combinar con otros patrones de diseño para hacer la implementación más sofisticada en la medida que la solución lo requiera. Swift es un lenguaje bastante robusto y flexible que permitir su óptima implementación eliminando escenarios de fugas de memoria o performance.
Thanks for your comment