Clean Architecture + SwiftUI

Andres Felipe Ocampo
4 min readFeb 3, 2021

--

VipUI

Hace ya 13 años del lanzamiento de UIKit con el SDK de iOS del 2008, y hemos estado creando nuesttas App’s con esta tecnología, simplemente espectacular!! como dirían algunos colegas!, y desde el primer día hemos estado buscando la arquitectura para usar en nuestras aplicaciones. Como bien sabemos, todo comienza con nuestro maravilloso MVC, y posteriormente pasamos a MVP, MVVM, VIPER, VEHEMONT, RIB, VIP, ELM.

Pero algo ha ocurrido recientemente, que ha impactado de manera significativa que en la mayoría de patrones arquitectónicos utilizados en iOS hace que pasen a la historia y no por malos sino por reajustes que los desarrolladores hemos estado pidiendo.

Estamos hablado de SwiftUI. Nos guste o no, este es el futuro del desarrollo de las Aplicaciones en iOS, MacOS, WatchOS, TvOS, App Clip, y un largo etcétera y esto es un cambio de las reglas de juego en terminos de los desafíos que enfrentabamos al diseñar Arquitectura de Software.

¿Cuáles son los cambios conceptuales?

UIKit es un marco imperativo impulsado por eventos. Se puede hacer referencia a cada vista en la jerarquía, actualizar su apariencia cuando se carga la vista o como una reacción a un evento (ToucheBegin, un toque en el botón o un nuevo dato disponible para mostrar en una UITableView). Usamos callbacks, delegados, Target-actions para manejar los eventos.

Ahora muchas cosas se han esfumado literalmente. SwiftUI es un framework declarativo impulsado por el estado(comorrlll ??). No podemos hacer referencia a ninguna vista en la jerarquía, ni podemos mutar directamente una vista como reacción a un evento. En cambio. mutamos el State vinculado a la vista. Delegados, Target-actions, KVO, todo esto ha sido reemplazado por closures y bindings.

Cada vista en SwiftUI es una struct que se puede crear muchas veces y más rápido que un descendiente de UIView análogo. Es struct mantiene referencias al State que alimanta la funcion body para representar la interfaz del usuario. (Las vistas son una función del estado, no una secuencia de eventos).

Está claro pués, una vista en SwiftUI es solo una función de programación. Le proporcionas la entrada (State): dibuja la salida. Y la única forma de cambiar la salida es cambiar la entrada: no podemos tocar el algoritmo (la función del cuerpo) agregando o eliminando subvistas; todas las posibles alteraciones en la interfaz de usuario mostrada deben declararse en el cuerpo y no se pueden cambiar en tiempo de ejecución.

En términos de SwiftUI, no estamos agregando o eliminando subvistas, sino habilitando o deshabilitando diferentes partes de la IU en el algoritmo de diagrama de flujo predefinido.

MVVM es la nueva arquitectura estándar

SwiftUI viene con MVVM incorporado. En el caso más simple, donde View no depende de ningún estado externo, las variables locales que permiten la mutación se definen como @State, estas variables asumen el papel de ViewModel, proporcionando un mecanismo de suscripción @Binding, para actualizar la interfaz del usuario cada vez que cambia si estado.

Para escenarios más complejos, las Views pueden hacer referencia a un tipo externo ObservableObject, que en este caso tambien es distintivo de ViewModel.

Waww!, ya no necesitamos un ViewController

Voy a colocar un ejemplo muy rápido para entender MVVM para una aplicación desarrollada con SwiftUI

Modelo: Contenedor de datos

struct Person{
let name: String
let lastname: String
let alias: String
}

Vista: Una vista en SwiftUI

struct PersonsList: View {

@ObservedObject var viewModel: ViewModel

var body: some View {
List(viewModel.persons) { person in
Text(person.name)
Text(person.lastname)
Text(person.alias)
}
.onAppear {
self.viewModel.loadPersons()
}
}
}

ViewModel: es necesario encapsular la lógica de negocio en un ObservableObject y éste permite a la View observar los cambios de estado

class ViewModel: ObservableObject {
@Published private(set) var persons: [Person] = []

private let service: WebService

func loadPersons() {
service.getPersons { [weak self] result in
self?.persons = result.value ?? []
}
}
}

En este ejemplo simplificado, cuando la View aparece en la pantalla, nuestro onAppear(es el ViewDidLoad de UIKit) realiza la llamada que se loadPersons() al ViewModel, lo que activa la llamada de red para cargar los datos del WebService, el ViewModel recibe los datos de la devolución de las llamada y envía la actualización de los datos a través de @Publised que esta siendo observada por la View. (Waaa! espectacular!).

Clean Architectur

Voy a referirme a Uncle Bob’s Clean Architecture,le creador de VIP

Al separar el software en capas y cumplir con la regla de dependencia, creará un sistema que es intrínsecamente comprobable, con todos los beneficios que ello implica.

Clean Architecture es bastante liberal sobre el número de capas que deberíamos introducir porque esto depende del dominio de la aplicación.

Pero en el escenario más común para una aplicación móvil, necesitaremos tres capas:

  • Capa de presentación
  • Capa de lógica
  • Capa de acceso a datos

Entonces, si redibujamos el esquema de la arquitectura limpia a través de la peculiaridad de SwiftUI, se nos ocurriría algo como esto:

Podemos seguir trabajar con los protocolos entre las clases abstractas para realizar los Test unitarios, además podemos incorporar Combine para la capa Provider / Data Access.

¿Se aplican VIPER, RIB y VIP a SwiftUI?

Hay muchas ideas y conceptos geniales que podemos tener como referentes de estas arquitecturas, pero su implementación puede que no sea compatible con SwiftUI.

  1. Como ya sabemos no tenemos necesidad práctica de tener un Router
  2. El Diseño completamente nuevo del flujo de datos en SwiftUI junto con el soporte nativo de los enlaces reduce el código de configuración requerido hasta el punto que el Presenter hace lo minimo posible, no hace cosas muy útiles.
  3. Junto con la disminución de modulos pordríamos considerar que el Buider / Assembly tampoco tiene sentido y esto haría que nuestro patrón se desmoronara(os dejo aquí un articulo de como construir VIPER con SwiftUI).

--

--

Andres Felipe Ocampo
Andres Felipe Ocampo

Written by Andres Felipe Ocampo

Digital Manager and Sr Lead iOS Engineer

Responses (1)