Descripción general de la arquitectura Clean Swift

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.

  1. El usuario ingresó su nombre de usuario y contraseña, hizo clic en el botón de autorización
  2. ViewController dispara IBAction , después de lo cual se crea una estructura con los datos de usuario ingresados ​​en TextFields, (Modelos -> Solicitud)
  3. La estructura creada se pasa al método fetchUser en Interactor'e
  4. Interactor envía una solicitud a la red y recibe una respuesta sobre el éxito de la autorización
  5. 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
  6. Presenter formatea los datos según sea necesario y los devuelve (Modelos -> ViewModel) al método displayUser en ViewController'e
  7. 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


  1. Comprender la arquitectura Clean Swift (Usted está aquí)
  2. Enrutador y paso de datos en la arquitectura Clean Swift
  3. Trabajadores de la arquitectura Clean Swift
  4. Pruebas unitarias en arquitectura Clean Swift
  5. Un ejemplo de una arquitectura simple de tienda en línea Clean Swift

Todos los componentes de la escena: enlace
Ayuda para escribir un artículo: Bastien

Source: https://habr.com/ru/post/453986/


All Articles