Enrutador y datos que pasan arquitectura Clean Swift

Hola lector

En un artículo anterior , hablé sobre el ciclo VIP de la arquitectura Clean Swift . Ahora tocaremos uno de los temas más importantes: la transición y la transferencia de datos entre escenas.



Teoría


El componente Router es responsable de la lógica de navegación y transferencia de datos, que es parte de la escena (opcional, por supuesto). Se inicializa en ViewController , junto con el Interactor y el Presentador .

El enrutador implementa dos protocolos: RoutingLogic y DataPassing , que completaremos con nuestra funcionalidad. RoutingLogic debe contener los métodos responsables de la transición a una escena específica. DataPassing contiene la variable dataStore , que se refiere al Protocol DataStore . Interactor Scenes implementa el protocolo DataStore y trabaja con las variables almacenadas en él. El enrutador contiene un enlace al ViewController de su escena.

Usando el enlace al ViewController , el enrutador salta entre las escenas. Para hacer esto, puede usar Segue o crear una escena a la que desea hacer una transición, mediante programación. El método utilizado no es importante, lo principal para nosotros es tener un enlace a una instancia de la clase ViewController a la que nos estamos cambiando.

Usando el enlace al DataStore, transferiremos datos desde el Interactor de una escena al Interactor de la escena a la que estamos cambiando. Y, como se mencionó anteriormente, es el enrutador el que necesita saber cómo hacer esto.

Practica


Por ejemplo, transferiremos el texto de TextField a Label de otra escena. Consideremos dos formas de transición entre escenas: según Segue y mediante programación.

La clase Router contiene 3 grupos semánticos de métodos:

  1. Métodos de implementación de RoutingLogic (routeTo)
  2. Métodos responsables de la navegación (navegar a, transición sin Segue)
  3. Métodos para la transferencia de datos (passDataTo, si hay datos para transferir)



Si hacemos una transición a través de Segue , por ejemplo, cuando se hace clic en un botón, en ViewController debemos anular el método prepare (for: sender :). Esta extensión le permitirá llamar automáticamente a los métodos del enrutador con el nombre de Segue .

La preparación de sobrescritura (para: remitente :) es opcional cuando se trabaja con Segue. Puede excluirlo del código y llamar a performSegue (withIdentifier: sender :) en el método Router . Preparar solo es necesario si necesita usar Segue junto con la transferencia de datos.

final class HomeViewController: UIViewController {
// ...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Segue
if let scene = segue.identifier {
// , Router
// router?.routeToNAME(segue:)
let selector = NSSelectorFromString("routeTo\(scene)WithSegue:")
// ,
// Segue
if let router = router, router.responds(to: selector) {
router.perform(selector, with: segue)
}
}
}
// ...
}


Ahora finalmente llegamos a lo más interesante: el código del enrutador . El ejemplo contiene comentarios, por lo que solo consideraremos los puntos clave.

En este enrutador, trabajamos con dos escenas: Inicio y Detalle . La transición desde la escena Home también se maneja de dos maneras: por Segue y mediante programación . Los datos se transfieren desde la escena Inicio a la escena Detalle .

Todos los métodos en el protocolo RoutingLogic deben nombrarse de acuerdo con el principio routeToNAME , donde NAME es el nombre del Segue (Identificador) que especificamos al trabajar con Storyboard . Esto es necesario no solo para la conveniencia y belleza del uso, sino también para nuestro selector de métodos en preparación (para: remitente :) ViewController , que redefinimos anteriormente.

También en la clase HomeRouter hay métodos que comienzan con navigationTo y passDataTo . Los primeros son responsables de la lógica de transición, mientras que los segundos son responsables de la transferencia de datos. Los métodos de navegación solo se crean si la transición se realiza mediante programación.

En el ejemplo, tenemos un método routeToDetail (segue :) . El parámetro segue es opcional ya que El método contiene una implementación que le permite llamarlo sin usar Segue . En ambos casos de transición, obtenemos valores no opcionales de nuestra escena HomeViewController y HomeDataStore'a Home , así como enlaces a la escena ViewController y DataStore Detail . Aquí vale la pena prestar atención al hecho de que detailDS es una variable y se pasa al método passDataToDetail usando el parámetro pass-through (inout). Esto es importante porque sin inout, tendremos que marcar todos los protocolos de DataStore como "posibles de implementar solo con clases" (protocolo DetailDataStore: class), y esto conlleva muchas dificultades, incluida la captura de enlaces fuertes.

import UIKit
/// @objc
/// prepare View Controller'e
@objc protocol HomeRoutingLogic {
/// Detail View Controller
func routeToDetail(segue: UIStoryboardSegue?)
}
protocol HomeDataPassing {
var dataStore: HomeDataStore? { get }
}
final class HomeRouter: NSObject, HomeRoutingLogic, HomeDataPassing {
// MARK: - Private
// MARK: - Public
weak var viewController: HomeViewController?
var dataStore: HomeDataStore?
// MARK: - HomeRoutingLogic
func routeToDetail(segue: UIStoryboardSegue?) {
if let segue = segue {
//
// Segue
// Detail View Controller
// Data Store Router'e
guard
let homeDS = dataStore,
let detailVC = segue.destination as? DetailViewController,
var detailDS = detailVC.router?.dataStore
else { fatalError("Fail route to detail") }
// , , ,
// ""
passDataToDetail(source: homeDS, destination: &detailDS)
} else {
// ,
// Segue
// Detail View Controller Storyboard'a
// Data Store Router'e
guard
let viewController = viewController,
let homeDS = dataStore,
let storyboard = viewController.storyboard,
let detailVC = storyboard.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
var detailDS = detailVC.router?.dataStore
else { fatalError("Fail route to detail") }
passDataToDetail(source: homeDS, destination: &detailDS)
// , ""
navigateToDetail(source: viewController, destination: detailVC)
}
}
// MARK: - Navigation
private func navigateToDetail(source: HomeViewController, destination: DetailViewController) {
source.navigationController?.pushViewController(destination, animated: true)
}
// MARK: - Passing data
/// destination inout,
/// Data Store
private func passDataToDetail(source: HomeDataStore, destination: inout DetailDataStore) {
// HomeDataStore DetailDataStore
destination.message = source.message
}
}


Conclusión


Eso es todo. ¡Gracias por leer hasta el final! A continuación, dejaré un enlace al proyecto si desea probar el artículo en acción.

Serie de artículos


  1. Descripción general de la arquitectura Clean Swift
  2. Enrutador y paso de datos en arquitectura Clean Swift (usted está aquí)
  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

Enlace al proyecto
Ayuda para escribir un artículo: Bastien

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


All Articles