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:
- Métodos de implementación de RoutingLogic (routeTo)
- Métodos responsables de la navegación (navegar a, transición sin Segue)
- 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
- Descripción general de la arquitectura Clean Swift
- Enrutador y paso de datos en arquitectura Clean Swift (usted está aquí)
- 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
Enlace al proyectoAyuda para escribir un artículo:
Bastien