iOS: dependency injection en UIViewController

Estoy buscando una forma elegante de implementar DI en los controlleres de vista cuando se trabaja con segues. Si tengo un controller A que eventualmente carga el controller B, me gustaría probar este comportamiento. ¿Cuál sería una buena manera de implementarlo?

Sería de gran ayuda si hubiera una manera directa de utilizar la inyección de "constructor" al usar storyboards, pero lamentablemente no es así, ya que el marco está manejando la initialization de VC para nosotros.

Dependiendo del tamaño y el context del proyecto en general, puede optar por un marco completo como Typhoon (que es muy bueno en mi experiencia) o intentar algo más simple, como usar un mediador para manejar todos los segues entre los VC.

He escrito sobre el último método aquí: http://cocoapatterns.com/passing-data-between-view-controllers/ y aquí http://cocoapatterns.com/ios-view-controller-transitions-mediator-pattern/

Especialmente cuando se combina con el patrón de layout de estado, el mediador proporciona una forma central de manejar la inyección durante segues y también ayuda con el mantenimiento y la extensibilidad de la base de código.

Considere usar la biblioteca de inyecciones de dependencia de Typhoon ligera. Typhoon funciona aprovechando las capacidades de reenvío de posts del time de ejecución de Objective-C. Puede definir uno o más 'ensamblados' de la siguiente manera:

  • En los ensamblados de time de construcción, devuelva TyphoonDefinition s: recetas para ensamblar componentes.
  • En time de ejecución, utilizamos la interfaz de ensamblaje para resolver componentes incorporados, con sus dependencies inyectadas de acuerdo con las reglas definidas.

Ejemplo: ApplicationAssembly.swift

 public class ApplicationAssembly: TyphoonAssembly { /* * These are modules - assemblies collaborate to provie components to this one. * At runtime you can instantiate Typhoon with any assembly that satisfies the * module interface. */ var coreComponents : CoreComponents! var themeAssembly : ThemeAssembly! public dynamic func weatherReportController() -> AnyObject { return TyphoonDefinition.withClass(WeatherReportViewController.self) { (definition) in definition.useInitializer("initWithWeatherClient:weatherReportDao:cityDao:assembly:") { (initializer) in initializer.injectParameterWith(self.coreComponents.weatherClient()) initializer.injectParameterWith(self.coreComponents.weatherReportDao()) initializer.injectParameterWith(self.coreComponents.cityDao()) initializer.injectParameterWith(self) } }; } public dynamic func addCityViewController() -> AnyObject { return TyphoonDefinition.withClass(AddCityViewController.self) { (definition) in definition.useInitializer("initWithNibName:bundle:") { (initializer) in initializer.injectParameterWith("AddCity") initializer.injectParameterWith(NSBundle.mainBundle()) } definition.injectProperty("cityDao", with:self.coreComponents.cityDao()) definition.injectProperty("weatherClient", with:self.coreComponents.weatherClient()) definition.injectProperty("theme", with:self.themeAssembly.currentTheme()) definition.injectProperty("rootViewController", with:self.rootViewController()) } } } 

Este estilo de Dependency Injection fue diseñado para ser no invasivo y permitir múltiples componentes del mismo tipo (una limitación en algunas bibliotecas de DI de tipo centrado), al mismo time que permite la finalización y refactorización del código IDE. Y también para evitar el uso de "cadenas mágicas" (aunque desafortunadamente en Swift, los selectores son cadenas).

Procediendo de un controller a otro:

El ensamblaje mismo puede inyectarse, de modo que nuestro WeatherReportController pueda presentar un CitiesListController llamando a assembly.citiesListController . También podemos tener una mezcla de dependencies estáticas y de time de ejecución utilizando los arguments de time de ejecución de Typhoon, lo que ahorra el tedio de tener que escribir manualmente su propia fábrica para esto.

Guiones charts:

Para los segues de tablas de historias, las properties de Typhoon o las dependencies de methods se pueden inyectar como parte de la segue .

introduzca la descripción de la imagen aquí

El tifón funciona con Swift u Objective-C y es particularmente popular en Australia, Japón y Rusia.

Pruebas:

A continuación, le indicamos cómo probar la lógica del controller de vista con tablas de historia e dependency injection.

En la testing de unidad, puede inyectar su subclass UIStoryboard como propiedad de "storyboard" (intente setValue: forKey ya que es propiedad de solo lectura. De lo contrario, puede instanciar su viewController desde su subclass UIStoryboard, otra forma de completar la propiedad 'storyboard' pero mucho más detallado) Después de replace la propiedad .storyboard, puede espiar el método "instantiateViewControllerForStoryboardIdentifier" y comprobar que se llama con el identificador correcto.