Hola lector
En este artículo hablaré sobre la arquitectura de las aplicaciones de iOS: 
Clean Swift . Consideraremos los principales puntos teóricos y analizaremos un ejemplo en la práctica.

Teoría
Para comenzar, analizaremos la terminología básica de la arquitectura. En 
Clean Swift, una aplicación consta de escenas, es decir, Cada pantalla de aplicación es una escena. La interacción principal en la escena pasa por un bucle secuencial entre los componentes de 
ViewController -> 
Interactor -> 
Presenter . Esto se llama ciclo 
VIP .
El puente entre los componentes es el archivo 
Modelos , que almacena los datos transmitidos. También hay un 
enrutador , que es responsable de transferir y transferir datos entre escenas, y 
Worker , que se hace cargo de parte de 
la lógica de 
Interactor .

Vista
Guiones gráficos, XIB o elementos de la IU escritos a través del código.
ViewController
Responsable solo de la configuración e interacción con 
View . El controlador no debe contener ninguna lógica de negocios, interacciones de red, informática, etc.
Su tarea es procesar eventos con 
Ver , mostrar o enviar datos (sin procesamiento y comprobaciones) en 
Interactor .
Interactractor
Contiene la lógica de negocios de la escena.
Funciona con la red, la base de datos y los módulos del dispositivo.
Interactor recibe una solicitud de 
ViewController (con datos o vacía), la procesa y, si es necesario, transfiere nuevos datos a 
Presenter .
Presentador
Se dedica a la preparación de datos para su visualización.
Como ejemplo, agregue una máscara a un número de teléfono o escriba la primera letra en el título de la capital.
Procesa los datos recibidos de 
Interactor y luego los devuelve al 
ViewController .
Modelos
Un conjunto de estructuras para transferir datos entre los componentes del ciclo 
VIP . Cada círculo del ciclo tiene 3 tipos de estructuras:
- Solicitud : estructura de datos (texto de TextField, etc.) para transferir de ViewController a Interactor
- Respuesta : estructura con datos (descargados de la red, etc.) para transferir de Interactor a Presentador
- ViewModel : estructura con datos procesados (formato de texto, etc.) en Presenter para transferirlos nuevamente a ViewController
Trabajador
Descarga 
Interactor , asumiendo una parte de la lógica empresarial de la aplicación si 
Interactor está creciendo rápidamente.
También puede crear 
Trabajadores que sean comunes a todas las escenas, si su funcionalidad se usa en varias escenas.
Como ejemplo, en 
Worker, puede hacer la lógica de trabajar con una red o base de datos.
Enrutador
Toda la lógica responsable de las transiciones y la transferencia de datos entre escenas se saca en el 
enrutador .
Para aclarar la imagen del ciclo 
VIP , daré un ejemplo estándar: autorización.
- El usuario ingresó su nombre de usuario y contraseña, hizo clic en el botón de autorización
- ViewController dispara IBAction , después de lo cual se crea una estructura con los datos de usuario ingresados en TextFields, (Modelos -> Solicitud)
- La estructura creada se pasa al método fetchUser en Interactor'e
- Interactor envía una solicitud a la red y recibe una respuesta sobre el éxito de la autorización
- En función de los datos recibidos, crea una estructura con el resultado (Modelos -> Respuesta) y se pasa al método presentUser en Presenter'e
- Presenter formatea los datos según sea necesario y los devuelve (Modelos -> ViewModel) al método displayUser en ViewController'e
- ViewController muestra los datos recibidos al usuario. En el caso de la autorización, se puede mostrar un error o se puede activar una transición a otra escena usando el enrutador
Por lo tanto, obtenemos una estructura única y consistente, con la distribución de responsabilidades en componentes.
Practica
Ahora echemos un vistazo a un pequeño ejemplo práctico que muestra cómo va el ciclo 
VIP . En este ejemplo, simularemos la carga de datos al abrir una escena (pantalla). Marqué las secciones principales del código con comentarios.
Todo el ciclo 
VIP está vinculado a protocolos, lo que brinda la capacidad de reemplazar cualquier módulo sin interrumpir la aplicación.
Se crea un protocolo 
DisplayLogic para 
ViewController , un enlace que se pasa a 
Presenter para su posterior recuperación. Para 
Interactor , se crean dos protocolos 
BusinessLogic , que se encargan de llamar a los métodos desde 
ViewController y 
DataSource , para almacenar datos y transferir otra escena a través de 
Router a 
Interactor . El presentador se suscribe al protocolo 
PresentationLogic para llamar desde 
Interactor . El elemento de conexión de todo esto es 
Modelos . Contiene estructuras con la ayuda de las cuales se intercambia información entre los componentes del ciclo 
VIP . Comenzaremos el análisis de código con él.

Modelos
En el siguiente ejemplo, para la escena 
Home , creé un archivo 
HomeModels que contiene un conjunto de consultas para el bucle 
VIP .
La solicitud 
FetchUser será responsable de cargar los datos del usuario, lo que consideraremos más a fondo.
|  | // Models | 
|  | /// VIP | 
|  | enum HomeModels { | 
|  |  | 
|  | /// VIP | 
|  | enum FetchUser { | 
|  |  | 
|  | /// Interactor View Controller | 
|  | struct Request { | 
|  | let userName: String | 
|  | } | 
|  |  | 
|  | /// Presentor Interactor | 
|  | struct Response { | 
|  | let userPhone: String | 
|  | let userEmail: String | 
|  | } | 
|  |  | 
|  | /// View Controller Presentor | 
|  | struct ViewModel { | 
|  | let userPhone: String | 
|  | let userEmail: String | 
|  | } | 
|  | } | 
|  | } | 
ViewController
Cuando se inicializa la clase, instanciamos las clases de 
Interactor y 
Presentador de esta escena y establecemos dependencias entre ellas.
Además, en 
ViewController'e solo hay un enlace a 
Interactor . Usando este enlace, crearemos una solicitud para el 
método fetchUser (request :) en 
Interactor para iniciar el ciclo 
VIP .
Aquí vale la pena prestar atención a cómo se produce la solicitud a 
Interactor . En el método 
loadUserInfromation () , creamos una instancia de la estructura 
Request , donde pasamos el valor inicial. Se puede tomar de 
TextField , tablas, etc. Se pasa una instancia de la estructura 
Request al método 
fetchUser (request :) , que se encuentra en el protocolo 
BusinessLogic de nuestro 
Interactor .
|  | // ViewController | 
|  | /// | 
|  | protocol HomeDisplayLogic: class { | 
|  |  | 
|  | /// | 
|  | func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel) | 
|  | } | 
|  |  | 
|  | final class HomeViewController: UIViewController { | 
|  |  | 
|  | /// Interactor'a | 
|  | var interactor: HomeBusinessLogic? | 
|  |  | 
|  | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | 
|  | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | 
|  | setup() | 
|  | } | 
|  |  | 
|  | required init?(coder aDecoder: NSCoder) { | 
|  | super.init(coder: aDecoder) | 
|  | setup() | 
|  | } | 
|  |  | 
|  | /// | 
|  | private func setup() { | 
|  | // VIP | 
|  | let interactor = HomeInteractor() | 
|  | let presenter = HomePresenter() | 
|  |  | 
|  | // | 
|  | interactor.presenter = presenter | 
|  | presenter.viewController = self | 
|  |  | 
|  | // Interactor View Controller | 
|  | self.interactor = interactor | 
|  | } | 
|  |  | 
|  | override func viewDidLoad() { | 
|  | super.viewDidLoad() | 
|  |  | 
|  | // | 
|  | fetchUser() | 
|  | } | 
|  |  | 
|  | /// Interactor | 
|  | private func loadUserInfromation() { | 
|  | // Interactor | 
|  | let request = HomeModels.FetchUser.Request(userName: "Aleksey") | 
|  |  | 
|  | // Interactor'a | 
|  | interactor?.fetchUser(request) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// HomeDisplayLogic | 
|  | extension HomeViewController: HomeDisplayLogic { | 
|  |  | 
|  | func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel) { | 
|  | print(viewModel) | 
|  | } | 
|  | } | 
Interactractor
Una instancia de la clase 
Interactor contiene un enlace al protocolo 
PresentationLogic , bajo el cual 
se firma 
Presenter .
El 
método fetchUser (request :) puede contener cualquier lógica de carga de datos. Por ejemplo, acabo de crear constantes, con datos supuestamente obtenidos.
En el mismo método, se crea una instancia de la estructura de 
Respuesta y se completa con los parámetros obtenidos anteriormente. 
La respuesta se pasa a 
PresentationLogic utilizando el 
método presentUser (respuesta :) . En otras palabras, aquí obtuvimos los datos sin procesar y los pasamos a 
Presenter para su procesamiento.
|  | // Interactor | 
|  | /// Interactor'a | 
|  | protocol HomeBusinessLogic: class { | 
|  |  | 
|  | /// | 
|  | func fetchUser(_ request: HomeModels.FetchUser.Request) | 
|  | } | 
|  |  | 
|  | final class HomeInteractor: HomeBusinessLogic { | 
|  |  | 
|  | /// | 
|  | var presenter: HomePresentationLogic? | 
|  |  | 
|  | func fetchUser(_ request: HomeModels.FetchUser.Request) { | 
|  | // | 
|  | // | 
|  | let userPhone = "+7 (999) 111-22-33" | 
|  | let userEmail = "im@alekseypleshkov.ru" | 
|  | // ... | 
|  | // Presentor' | 
|  | let response = HomeModels.FetchUser.Response(userPhone: userPhone, userEmail: userEmail) | 
|  |  | 
|  | // Presentor' | 
|  | presenter?.presentUser(response) | 
|  | } | 
|  | } | 
Presentador
Tiene un enlace al protocolo 
DisplayLogic , bajo el cual 
se firma 
ViewController . No contiene ninguna lógica empresarial, pero solo formatea los datos recibidos antes de mostrarlos. En el ejemplo, formateamos el número de teléfono, preparamos una instancia de la estructura 
ViewModel y la pasamos al 
ViewController usando el 
método displayUser (viewModel :) en el protocolo 
DisplayLogic , donde los datos ya se muestran.
|  | /// | 
|  | protocol HomePresentationLogic: class { | 
|  |  | 
|  | /// Interactor'a | 
|  | func presentUser(_ response: HomeModels.FetchUser.Response) | 
|  | } | 
|  |  | 
|  | final class HomePresenter: HomePresentationLogic { | 
|  |  | 
|  | /// View Controller'a | 
|  | weak var viewController: HomeDisplayLogic? | 
|  |  | 
|  | func presentUser(_ response: HomeModels.FetchUser.Response) { | 
|  | // | 
|  | let formattedPhone = response.userPhone.replacingOccurrences(of: "-", with: " ") | 
|  |  | 
|  | // ViewModel View Controller | 
|  | let viewModel = HomeModels.FetchUser.ViewModel(userPhone: formattedPhone, userEmail: response.userEmail) | 
|  |  | 
|  | // View Controller'a | 
|  | viewController?.displayUser(viewModel) | 
|  | } | 
|  | } | 
Conclusión
Con esta arquitectura, tuvimos la oportunidad de distribuir responsabilidades, mejorar la conveniencia de probar la aplicación, introducir la capacidad de reemplazo de secciones individuales de la implementación y el estándar para escribir código para trabajar en equipo.
Gracias por leer hasta el final.
Serie de artículos
- Comprender la arquitectura Clean Swift (Usted está aquí)
- Enrutador y paso de datos en la arquitectura Clean Swift
- Trabajadores de la arquitectura Clean Swift
- Pruebas unitarias en arquitectura Clean Swift
- Un ejemplo de una arquitectura simple de tienda en línea Clean Swift
Todos los componentes de la escena: 
enlaceAyuda para escribir un artículo: 
Bastien