Cada año, la plataforma iOS sufre muchos cambios, además, las bibliotecas de terceros trabajan regularmente para trabajar con la red, almacenar en caché los datos, representar la interfaz de usuario a través de JavaScript y más. En contraste con todas estas tendencias,
Pavel Gurov habló sobre la solución arquitectónica, que será relevante independientemente de las tecnologías que esté utilizando ahora o que utilizará en un par de años.
ApplicationCoordinator se puede utilizar para construir la navegación entre pantallas y al mismo tiempo resolver una serie de problemas. Debajo de la demostración del gato e instrucciones para la implementación más rápida de este enfoque.
Sobre el orador: Pavel Gurov está desarrollando aplicaciones iOS en Avito.
La navegación

Navegar entre pantallas es una tarea a la que se enfrenta el 100% de ustedes, sin importar lo que hagan: una red social, una llamada de taxi o un banco en línea. Así es como comienza la aplicación incluso en la etapa de prototipo, cuando ni siquiera sabe hasta el final: cómo se verán las pantallas, qué tipo de animaciones serán, si los datos se almacenarán en caché. Las pantallas pueden ser imágenes en blanco o estáticas, pero
la tarea de navegación aparece en la aplicación tan pronto como haya más de una de estas pantallas . Es decir, casi de inmediato.

Los métodos más comunes para construir la arquitectura de aplicaciones iOS: MVc, MVVm y MVp, describen cómo construir un módulo de pantalla única. También dice que los módulos pueden conocerse, comunicarse entre sí, etc. Pero se presta muy poca atención a los problemas de cómo se realizan las transiciones entre estos módulos, quién decide sobre estas transiciones y cómo se transmiten los datos.
UlStoryboard + segues
iOS listo para usar proporciona varias formas de mostrar el siguiente escenario de pantalla:
- El conocido UlStoryboard + sigue , cuando designamos todas las transiciones entre pantallas en un metaarchivo, y luego las llamamos. Todo es muy conveniente y genial.
- Contenedores, como el UINavigationController. UITabBarController, UIPageController o, posiblemente, contenedores autoescritos que se pueden usar tanto mediante programación como junto con StoryBoards.
- Método presente (_: animado: finalización :). Este es solo un método de la clase UIController.
No hay problemas con estas herramientas en sí mismas. El problema es exactamente cómo se usan comúnmente. El método UINavigationController, performSegue, prepareForSegue, presentViewController son todos métodos de propiedad de la clase UIViewController. Apple sugiere usar estas herramientas dentro del mismo UIViewController.

Prueba de ello es lo siguiente.

Estos son comentarios que aparecen en su proyecto si crea una nueva subclase de UIViewController utilizando una plantilla estándar. Está escrito directamente: si usa segues y necesita transferir datos a la siguiente pantalla según el escenario, debe: obtener este ViewController de segue; saber de qué tipo será; transmitirlo a este tipo y pasar sus datos allí.
Este enfoque a los problemas en la construcción de navegación.
1. Conectividad rígida de pantallasEsto significa que la pantalla 1 sabe acerca de la existencia de la pantalla 2. No solo él sabe acerca de su existencia, sino que también puede crearla, o tomarla de la segue, sabiendo de qué tipo es, y transfiere algunos datos a ella.
Si necesitamos, en algunas circunstancias, mostrar la pantalla 3 en lugar de la pantalla 2, entonces tendremos que conocer la nueva pantalla 3 de la misma manera para coser en el controlador de pantalla 1. Todo se vuelve aún más difícil si los controladores 2 y 3 se pueden llamar desde varios lugares más, no solo de la pantalla 1. Resulta que el conocimiento de la pantalla 2 y 3 tendrá que ser cosido en cada uno de estos lugares.
Hacer esto es otra mitad del problema, los problemas principales comenzarán cuando sea necesario realizar cambios en estas transiciones o apoyar todo esto.
2. Reordenar los controladores de scriptEsto tampoco es tan simple debido a la conexión. Para intercambiar dos ViewControllers, no será suficiente ingresar al UlStoryboard e intercambiar 2 imágenes. Tendrá que abrir el código para cada una de estas pantallas, transferirlo a la configuración de la siguiente y cambiar sus lugares, lo que no es muy conveniente.
3. Transferencia de datos según el escenario.Por ejemplo, al elegir algo en la pantalla 3, necesitamos actualizar la Vista en la pantalla 1. Dado que inicialmente no tenemos más que un ViewController, tendremos que conectar de alguna manera los dos ViewControllers, no importa cómo, por delegación o de alguna manera todavía Será aún más difícil si, de acuerdo con la acción en la pantalla 3, será necesario actualizar no una pantalla, sino varias a la vez, por ejemplo, la primera y la segunda.

En este caso, no se puede prescindir de la delegación, porque la delegación es una relación uno a uno. Alguien dirá, usemos notificación, alguien, a través de un estado compartido. Todo esto dificulta la depuración y el seguimiento de los flujos de datos en nuestra aplicación.
Como dicen, es mejor ver una vez que escuchar 100 veces. Veamos un ejemplo específico de esta aplicación Avito Services Pro. Esta aplicación es para profesionales en el sector de servicios, en el cual es conveniente rastrear sus pedidos, comunicarse con los clientes, buscar nuevos pedidos.
Escenario: elegir una ciudad para editar un perfil de usuario.

Aquí hay una pantalla de edición de perfil, como en muchas aplicaciones. Estamos interesados en elegir una ciudad.
¿Qué está pasando aquí?
- El usuario hace clic en la celda con la ciudad, y la primera pantalla decide que es hora de agregar la siguiente pantalla a la pila de navegación. Esta es una pantalla con una lista de ciudades federales (Moscú y San Petersburgo) y una lista de regiones.
- Si el usuario selecciona una ciudad federal en la segunda pantalla, entonces la segunda pantalla comprende que el guión se ha completado, reenvía la ciudad seleccionada a la primera y la pila de Navegación vuelve a la primera pantalla. El guión se considera completo.
- Si el usuario selecciona un área en la segunda pantalla, la segunda pantalla decide que debe prepararse una tercera pantalla, en la que vemos una lista de ciudades en esta área. Si el usuario selecciona una ciudad, esta ciudad se envía a la primera pantalla, tira la pila de Navegación y la secuencia de comandos se considera completa.
En este diagrama, los problemas de conectividad que mencioné anteriormente se muestran como flechas entre el ViewController. Nos libraremos de estos problemas ahora.
¿Cómo hacemos esto?- Nos prohibimos dentro del UIViewController acceder a los contenedores , es decir, self.navigationController, self.tabBarController u otros contenedores personalizados que haya realizado como extensión de propiedad. Ahora no podemos tomar nuestro contenedor del código de la pantalla y pedirle que haga algo.

- Nos prohibimos dentro del UIViewController llamar al método performSegue y escribir código en el método prepareForSegue, que tomaría la pantalla que sigue al script y lo configurará. Es decir, ya no trabajamos con segue (con transiciones entre pantallas) dentro del UIViewController.

- También prohibimos cualquier mención de otros controladores dentro de nuestro controlador específico : sin inicializaciones, transferencias de datos, y eso es todo.

Coordinador
Como eliminamos todas estas responsabilidades del UIViewController, necesitamos una nueva entidad que las realice. Crea una nueva clase de objetos y llámalo coordinador.

El coordinador es solo un objeto ordinario al que pasamos al inicio del NavigationController y llamamos al método Start. Ahora no piense en cómo se implementa, solo mire cómo cambiará el escenario para elegir una ciudad en este caso.
Ahora no comienza con el hecho de que estamos preparando la transición a una pantalla específica de NavigationController, pero llamamos al método Start en el coordinador, pasándolo antes en el inicializador NavigationController. El coordinador entiende que es hora de que NavigationController inicie la primera pantalla, lo cual hace.
Además, cuando el usuario selecciona una celda con una ciudad, este evento se pasa al coordinador. Es decir, la pantalla en sí misma no sabe nada; después, como dicen, al menos una inundación. Envía este mensaje al coordinador, y luego el coordinador reacciona a esto (ya que tiene un NavigationController), que envía el siguiente paso: esta es la elección de las regiones.
Luego, el usuario hace clic en "Región", exactamente la misma imagen, la pantalla en sí no resuelve nada, solo le dice al coordinador que se abre la siguiente pantalla.
Cuando el usuario selecciona una ciudad específica en la tercera pantalla, esta ciudad también se transfiere a la primera pantalla a través del coordinador. Es decir, se envía un mensaje al coordinador de que se ha seleccionado una ciudad. El coordinador envía este mensaje a la primera pantalla y lanza la pila de navegación a la primera pantalla.
Tenga en cuenta que los
controladores ya no se comunican entre sí , deciden quién será el próximo y no se transmiten datos entre ellos. Además, no saben nada de su entorno.

Si consideramos la aplicación dentro del marco de una arquitectura de tres capas, entonces el ViewController debería encajar perfectamente en la capa de Presentación y llevar lo menos posible la lógica de la aplicación.
En este caso, usamos el coordinador para extraer la lógica de las transiciones a la capa superior y eliminar este conocimiento del ViewController.
Demo
Una presentación y un
proyecto de demostración están disponibles en Github, a continuación se muestra una demostración durante la charla.
Este es el mismo escenario: editar un perfil y elegir una ciudad en él.
La primera pantalla es la pantalla de edición del usuario. Muestra información sobre el usuario actual: nombre y ciudad seleccionada. Hay un botón "Elegir una ciudad". Cuando hacemos clic en él, llegamos a la pantalla con una lista de ciudades. Si seleccionamos una ciudad allí, entonces la primera pantalla muestra esta ciudad.
Veamos ahora cómo funciona esto en el código. Comencemos con el modelo.
struct City { let name: String } struct User { let name: String var city: City? }
Los modelos son simples:
- Una estructura de ciudad que tiene un nombre de campo, cadena;
- Un usuario que también tiene un nombre y ciudad de propiedad.
El siguiente es
StoryBoard . Comienza con un NavigationController. En principio, aquí están las mismas pantallas que estaban en el simulador: una pantalla de edición de usuario con una etiqueta y un botón y una pantalla con una lista de ciudades, que muestra una tableta con ciudades.
Pantalla de edición de usuario
import UIKit final class UserEditViewController: UIViewController, UpdateableWithUser { // MARK: - Input - var user: User? { didSet { updateView() } } // MARK: - Output - var onSelectCity: (() -> Void)? @IBOutlet private weak var userLabel: UILabel? @IBAction private func selectCityTap(_ sender: UIButton) { onSelectCity?() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) updateView() } private func updateView() { userLabel?.text = "User: \(user?.name ?? ""), \n" + "City: \(user?.city?.name ?? "")" } }
Aquí hay una propiedad Usuario, este es el usuario que se transmite fuera, el usuario que editaremos. Establecer usuario aquí hace que se llame al bloque didSet, lo que lleva a una llamada al método local updateView (). Todo lo que hace este método es simplemente poner información sobre el usuario en la etiqueta, es decir, mostrar su nombre y el nombre de la ciudad en la que vive este usuario.
Lo mismo sucede en el método viewWillAppear ().
El lugar más interesante es el controlador para hacer clic en el botón de selección de ciudad selectCityTap ().
Aquí, el controlador en sí no resuelve nada : no crea ningún controlador, no llama a ningún segue. Todo lo que hace es devolución de llamada: esta es la segunda propiedad de nuestro ViewController. La devolución de llamada onSelectCity no tiene parámetros. Cuando el usuario hace clic en el botón, esto hace que se llame a esta devolución de llamada.
Pantalla de selección de ciudad
import UIKit final class CitiesViewController: UITableViewController { // MARK: - Output - var onCitySelected: ((City) -> Void)? // MARK: - Private variables - private let cities: [City] = [City(name: "Moscow"), City(name: "Ulyanovsk"), City(name: "New York"), City(name: "Tokyo")] // MARK: - Table - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cities.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = cities[indexPath.row].name return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { onCitySelected?(cities[indexPath.row]) } }
Esta pantalla es un UITableViewController. La lista de ciudades aquí es fija, pero puede provenir de otro lugar. Además (// MARK: - Table -) es un código de tabla bastante trivial que muestra una lista de ciudades en celdas.
El lugar más interesante aquí es el controlador didSelectRowAt IndexPath, un método bien conocido para todos. Aquí la pantalla en sí misma no resuelve nada. ¿Qué sucede después de seleccionar la ciudad? Simplemente llama a una devolución de llamada con un solo parámetro "ciudad".
Esto termina el código para las pantallas mismas. Como vemos, no saben nada sobre su entorno.
Coordinador
Pasemos al enlace entre estas pantallas.
import UIKit protocol UpdateableWithUser: class { var user: User? { get set } } final class UserEditCoordinator { // MARK: - Properties private var user: User { didSet { updateInterfaces() } } private weak var navigationController: UINavigationController? // MARK: - Init init(user: User, navigationController: UINavigationController) { self.user = user self.navigationController = navigationController } func start() { showUserEditScreen() } // MARK: - Private implementation private func showUserEditScreen() { let controller = UIStoryboard.makeUserEditController() controller.user = user controller.onSelectCity = { [weak self] in self?.showCitiesScreen() } navigationController?.pushViewController(controller, animated: false) } private func showCitiesScreen() { let controller = UIStoryboard.makeCitiesController() controller.onCitySelected = { [weak self] city in self?.user.city = city _ = self?.navigationController?.popViewController(animated: true) } navigationController?.pushViewController(controller, animated: true) } private func updateInterfaces() { navigationController?.viewControllers.forEach { ($0 as? UpdateableWithUser)?.user = user } } }
El coordinador tiene dos propiedades:
- Usuario: el usuario que editaremos;
- El NavigationController al que pasar al inicio.
Hay un simple init () que llena estas propiedades.
El siguiente es el método start (), que hace que se
llame al método
ShowUserEditScreen () . Detengámonos en ello con más detalle. Este método saca el controlador de UIStoryboard, lo pasa a nuestro usuario local. Luego pone la devolución de llamada SelectCity y empuja este controlador a la pila de Navegación.
Después de que el usuario hace clic en el botón, se activa la devolución de llamada onSelectCity y esto provoca que se
llame al siguiente
método privado
ShowCitiesScreen () .
De hecho, hace casi lo mismo: levanta un controlador ligeramente diferente del UIStoryboard, coloca la devolución de llamada onCitySelected y lo empuja a la pila de navegación, eso es todo lo que sucede. Cuando el usuario selecciona una ciudad específica, se activa esta devolución de llamada, el coordinador actualiza el campo "ciudad" de nuestro usuario local y pasa la pila de navegación a la primera pantalla.
Como el usuario es una estructura, la actualización del campo "ciudad" en él lleva al hecho de que se llama al bloque didSet, respectivamente, se llama al método privado
updateInterfaces () . Este método recorre toda la pila de navegación e intenta implementar cada ViewController como el protocolo UpdateableWithUser. Este es el protocolo más simple, que tiene una sola propiedad: usuario. Si esto tiene éxito, entonces lo lanza al usuario actualizado. Por lo tanto, resulta que nuestro usuario seleccionado en la segunda pantalla salta automáticamente a la primera pantalla.
Todo está claro con el coordinador, y lo único que queda por mostrar aquí es el punto de entrada a nuestra aplicación. Aquí es donde todo comienza. En este caso, este es el método didFinishLaunchingWithOptions de nuestro AppDelegate.
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var coordinator: UserEditCoordinator! func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { guard let navigationController = window?.rootViewController as? UINavigationController else { return true } let user = User(name: "Pavel Gurov", city: City(name: "Moscow")) coordinator = UserEditCoordinator(user: user, navigationController: navigationController) coordinator.start() return true } }
Aquí el navigationController obtiene de UIStoryboard, se crea un Usuario, que editaremos, con un nombre y una ciudad específica. A continuación, creamos nuestro coordinador con User y navigationController. Llama al método start (). El coordinador se transfiere a la propiedad local, eso es básicamente todo. El esquema es bastante simple.
Entradas y salidas
Hay varios puntos en los que me gustaría profundizar más. Probablemente haya notado que la propiedad en userEditViewController está marcada con un comentario como Entrada, y las devoluciones de llamada de estos controladores están marcadas como Salida.
Una entrada es cualquier dato que pueda cambiar con el tiempo, así como algunos métodos de ViewController que se pueden llamar desde afuera. Por ejemplo, en UserEditViewController esta es una propiedad del usuario: el usuario mismo o su parámetro City pueden cambiar.
Una salida es cualquier evento que el controlador quiera comunicar al mundo exterior. En UserEditViewController, este es un clic en el botón onSelectCity, y en la pantalla de selección de ciudad, es un clic en una celda con una ciudad específica. La idea principal aquí es, repito, que el controlador no sabe nada y no hace nada acerca de estos eventos. Delega para decidir qué hacer, a otra persona.
En Objective-C, realmente no me gustaba escribir devoluciones de llamadas guardadas debido a su horrible sintaxis. Pero en Swift, esto es mucho más simple. El uso de devoluciones de llamada en este caso es una alternativa al conocido patrón de delegación en iOS. Solo aquí, en lugar de designar métodos en el protocolo y decir que el coordinador corresponde a este protocolo, y luego escribir estos métodos en algún lugar por separado, podemos crear de manera muy conveniente una entidad en un solo lugar, devolverle la llamada y hacerlo todo.
Es cierto que con este enfoque, a diferencia de la delegación, existe una estrecha conexión entre la esencia del coordinador y la pantalla, porque el coordinador sabe que existe una esencia específica de la pantalla.
Puede deshacerse de esto de la misma manera que en la delegación, utilizando protocolos.

Para evitar la conectividad, podemos
cerrar la entrada y salida de nuestro controlador con un protocolo .
Arriba está el protocolo CitiesOutput, que tiene exactamente un requisito: la devolución de llamada onCitySelected. A la izquierda hay un análogo de este esquema en Swift. Nuestro controlador cumple con este protocolo, determinando la devolución de llamada necesaria. Hacemos esto para que el coordinador no sepa sobre la existencia de la clase CitiesViewController. Pero en algún momento necesitará configurar la salida de este controlador. Para ponerlo todo en marcha, agregamos una fábrica al coordinador.

La fábrica tiene un método cityOutput (). Resulta que nuestro coordinador no crea un controlador y no lo obtiene de alguna parte. Una fábrica se lo arroja, lo que devuelve un objeto cerrado por el protocolo en el método, y él no sabe nada sobre qué clase es este objeto.
Ahora lo más importante: ¿por qué hacer todo esto?
¿Por qué necesitamos construir en otro nivel adicional cuando no hubo problemas de todos modos?Uno puede imaginar esta situación: un gerente vendrá a nosotros y le pedirá que haga una prueba A / B del hecho de que, en lugar de una lista de ciudades, tendríamos la opción de elegir una ciudad en el mapa. Si en nuestra aplicación la elección de la ciudad no estaba en un lugar, sino en diferentes coordinadores, en diferentes escenarios, tuvimos que coser una bandera en cada lugar, arrojarla afuera, en esta bandera izar uno u otro ViewController. Esto no es muy conveniente.
Queremos eliminar este conocimiento del coordinador. Por lo tanto, uno podría hacer esto en un solo lugar. En la propia fábrica, haríamos un parámetro mediante el cual la fábrica devuelve uno u otro controlador cerrado por el protocolo. Ambos tendrían una devolución de llamada en CitySelected, y al coordinador, en principio, no le importaría con cuál de estas pantallas trabajar: un mapa o una lista.
Composición VS Herencia
El siguiente punto en el que quería insistir es la composición contra la herencia.

- El primer método de cómo nuestro coordinador puede hacerse es hacer la composición cuando el NavigationController se le pasa desde el exterior y se almacena localmente como propiedad. Esto es como una composición: le agregamos un NavigationController como una propiedad.
- Por otro lado, existe la opinión de que todo está allí en el kit de interfaz de usuario, y no necesitamos reinventar la rueda. Simplemente puede tomar y heredar el UI NavigationController .
Cada opción tiene sus pros y sus contras, pero personalmente me parece que la
composición en este caso es más adecuada que la herencia. La herencia es generalmente un esquema menos flexible. Si necesitamos, por ejemplo, cambiar la navegación a, por ejemplo, UIPageController, en el primer caso podemos simplemente cerrarlos con un protocolo común, como "Mostrar la siguiente pantalla" y sustituir convenientemente el contenedor que necesitamos.
Desde mi punto de vista, el argumento más importante es que escondes del usuario final en la composición todos los métodos innecesarios. Resulta que es menos probable que tropiece. Dejas
solo la API necesaria , por ejemplo, el método de Inicio, y eso es todo. No tiene forma de llamar al método PushViewController, PopViewController, es decir, interferir de alguna manera con las actividades del coordinador. Todos los métodos de la clase padre están ocultos.
Storyboards
Creo que merecen una atención especial junto con segues. Personalmente,
apoyo segues , ya que te permiten familiarizarte rápidamente con el script. Cuando llega un nuevo desarrollador, no necesita escalar el código, Storyboards lo ayuda. Incluso si crea una interfaz con el código, puede dejar el ViewController vacío y crear la interfaz con el código, pero deje al menos las transiciones y todo el punto. Toda la esencia de Storyboards está en las transiciones en sí, y no en el diseño de la interfaz de usuario.
Afortunadamente, el
enfoque de coordinador no limita la elección de herramientas . Podemos usar coordinadores de forma segura junto con segues. Pero debemos recordar que ahora no podemos trabajar con segues dentro del UIViewController.

Por lo tanto, debemos anular el método onPrepareForSegue en nuestra clase. En lugar de hacer algo dentro del controlador, delegaremos estas tareas nuevamente al coordinador, a través de la devolución de llamada. Se llama al método onPrepareForSegue, usted no hace nada usted mismo, no sabe qué tipo de segue es, qué controlador de destino es, no le importa. Simplemente lo arroja todo en una devolución de llamada, y el coordinador lo resolverá. Él tiene este conocimiento, usted no necesita este conocimiento.
Para simplificar todo, puede hacer esto en una determinada clase Base para no anularlo en cada controlador tomado por separado. En este caso, será más conveniente para el coordinador trabajar con sus segues.
Otra cosa que me parece conveniente con el Storyboard es cumplir con la regla de que
un Storyboard es igual a un coordinador . Luego, puede simplificar mucho todo, crear una clase en general: el Coordinador de Storyboard, y generar el parámetro RootType en él, crear el controlador de navegación inicial en el Storyboard y envolver todo el script en él.

Como puede ver, aquí el coordinador tiene 2 propiedades: navigationController; El rootViewController de nuestro RootType es genérico. Durante la inicialización, le pasamos no un control de navegación específico, sino un Storyboard, del que obtienen nuestra navegación raíz y su primer controlador. De esta manera, ni siquiera tendremos que llamar a ningún método de Inicio. Es decir, usted creó un coordinador, él inmediatamente tiene Navegación e inmediatamente tiene Raíz. Puede mostrar la navegación modalmente, o tomar Root e ingresar a la navegación existente y continuar trabajando.
Nuestro UserEditCoordinator en este caso simplemente se convertiría en typealias, sustituyendo el tipo de su RootViewController en el parámetro genérico.
Transferencia de datos de script de vuelta
Hablemos de resolver el último problema, que describí al principio. Esta es la transferencia de datos de vuelta al script.

Considere el mismo escenario para elegir una ciudad, pero ahora será posible elegir no una ciudad, sino varias. Para mostrarle al usuario que ha seleccionado varias ciudades dentro de la misma región, mostraremos en la pantalla con una lista de regiones un pequeño número al lado del nombre de la región, mostrando el número de ciudades seleccionadas en esta región.
Resulta que la acción en un controlador (en el tercero) debería conducir a un cambio en la apariencia de varios otros a la vez. Es decir, en el primero debemos mostrar en la celda con la ciudad, y en el segundo debemos actualizar todos los números en las regiones seleccionadas.
El coordinador simplifica esta tarea mediante la transferencia de datos al script; ahora es una tarea tan simple como transferir datos hacia adelante de acuerdo con el script.
¿Qué está pasando aquí? El usuario selecciona una ciudad. Este mensaje se envía al coordinador. El coordinador, como ya he mostrado en la demostración, revisa toda la pila de navegación y envía datos actualizados a todas las partes interesadas. En consecuencia, ViewController puede actualizar su Vista con estos datos.
Refactorizando el Código Existente
¿Cómo refactorizar el código existente si desea incrustar este enfoque en una aplicación existente que tenga MVc, MVVm o MVp?

Tienes un montón de ViewController. Lo primero que debe hacer es dividirlos en escenarios en los que participan. En nuestro ejemplo, hay 3 escenarios: autorización, edición de perfil, cinta.

Ahora envolvemos cada escenario dentro de nuestro coordinador. De hecho, deberíamos poder iniciar estos scripts desde cualquier lugar de nuestra aplicación. Esto debe ser flexible: el
coordinador debe ser completamente autosuficiente .
Este enfoque de desarrollo proporciona mayor comodidad. Consiste en el hecho de que si actualmente está trabajando con un escenario específico, no necesita hacer clic en él cada vez que lo inicia. Puede iniciarlo rápidamente desde el principio, editar algo en él y luego eliminar este inicio temporal.
Una vez que hayamos decidido sobre nuestros coordinadores, debemos determinar qué escenario puede conducir al inicio de otro y hacer un árbol a partir de estos escenarios.

En nuestro caso, el árbol es simple: LoginCoordinator puede iniciar el coordinador de edición de perfil. Aquí, casi todo encaja, pero queda un detalle muy importante: nuestro esquema carece de un punto de entrada.

Este punto de entrada será un coordinador especial: coordinador de
aplicaciones . Es creado e iniciado por
AppDelegate , y luego ya controla la lógica a nivel de aplicación, es decir, qué coordinador comienza ahora.
Simplemente miramos un circuito muy similar, solo que tenía ViewController en lugar de coordinadores, y lo hicimos para que ViewController no supiera nada el uno del otro y no se pasara datos entre sí. En principio, lo mismo puede hacerse con los coordinadores. Podemos designar cierta entrada (método de inicio) y salida (devolución de llamada onFinish) en ellos.
Los coordinadores se vuelven independientes, reutilizables y fácilmente comprobables . Los coordinadores dejan de conocerse y se comunican, por ejemplo, solo con el Coordinador de la aplicación.
Debe tener cuidado, porque si su aplicación tiene suficientes scripts, entonces el Coordinador de aplicaciones puede convertirse en un gran objeto divino, sabrá sobre todos los scripts existentes; esto tampoco es muy bueno. Aquí ya debemos mirar, tal vez dividir a los coordinadores en subcoordinadores, es decir, pensar en tal arquitectura para que estos objetos no crezcan a tamaños increíbles.
Aunque el tamaño no siempre es motivo de refactorización .
Por donde empezar
Aconsejo comenzar de abajo hacia arriba: primero implemente scripts individuales.

Como solución alternativa, pueden iniciarse dentro del UIViewController. Es decir, siempre que no tenga Root u otros coordinadores, puede crear un coordinador y, como solución temporal, iniciarlo desde UIViewController, guardándolo localmente en la propiedad (como nextCoordinator está arriba). Cuando ocurre un evento, usted, como lo mostré en la demostración, crea una propiedad local, coloca al coordinador allí y llama al método Start. Todo es muy sencillo.
Luego, cuando todos estos coordinadores ya lo han hecho, el inicio de uno dentro del otro se ve exactamente igual. ¿Tiene una propiedad local o algún tipo de matriz de dependencias como coordinador? Pone todo esto allí para que no se escape y llame al método Start.
Resumen
- Las pantallas y los guiones independientes que no saben nada el uno del otro no se comunican entre sí. Intentamos lograr esto.
- Es fácil cambiar el orden de las pantallas en la aplicación sin cambiar los códigos de pantalla. Si todo se hace como debería, lo único que debería cambiar en la aplicación cuando cambia el script no es el código de la pantalla, sino el código del coordinador.
- Transferencia de datos simplificada entre pantallas y otras tareas que implican una conexión entre pantallas.
- Personalmente, mi momento favorito es que, para comenzar a usarlo, no es necesario agregar dependencias de terceros al proyecto y comprender el código de otras personas.
AppsConf 2018 ya es el 8 y 9 de octubre, ¡no te lo pierdas! Más bien, estudie el horario (o revíselo ) y reserve boletos. Naturalmente, se presta mucha atención a ambas plataformas: iOS y Android, además de informes sobre arquitectura que no están vinculados a una sola tecnología, y discusión de otros temas importantes relacionados con el mundo en torno al desarrollo móvil.