Tratando de adaptarse a una "architecture limpia" en una aplicación de iOS

Recientemente he estado repensando mi proyecto de architecture android , tratando de adaptarlo a una architecture más "limpia", específicamente, al tipo de layout sugerido por "Tío Bob" .

Lo que implica varias capas de abstracciones, un buen aislamiento de responsabilidades y una inversión de dependencia muy fuerte lograda mediante dependency injection; que, en última instancia, conduce a un sistema portátil muy desacoplado. Un candidato perfecto para ser probado mediante testings unitarias y testings de integración.

En mi implementación de Android, he terminado teniendo tres modules o capas diferentes:

dominio : entidades, interactores, presentadores (module java puro)

datos : (actúa como un repository para suministrar los datos al dominio) (module de biblioteca de Android)

presentación : elementos relacionados con la interfaz de usuario, fragments, actividades, vistas, etc. (module de aplicaciones de Android)

Por lo tanto, estoy tratando de averiguar cuál sería el mejor enfoque en el ecosistema de iOS. Intenté crear un proyecto con múltiples objectives para lograr la misma solución:

dominio : objective de línea de command (que parece muy extraño, pero creo que es el objective rápido más puro disponible)

datos : marco de cocoa táctil

presentación : marco de toque de cocoa

Con este enfoque, puedo usar estos objectives de la forma que lo hice con los modules de Android. Pero la primera advertencia que he encontrado es que tengo que agregar manualmente cada file nuevo al destino dependiente.

Pero mi conocimiento es muy limitado en proyectos con múltiples objectives. Me refiero a que nunca he creado una aplicación de iOS con múltiples objectives. Así que no sé si la solución sería usar un marco (cocoa touch / cocoa) como objective en lugar de un module de línea de command para la capa de dominio.

Cualquier idea sería realmente apreciada.

¡Gracias!

La architecture limpia de tío Bob se aplica absolutamente a iOS, Swift y Obj-C. La architecture es agnóstico del lenguaje. El propio tío Bob codifica principalmente en Java, pero en sus conversaciones rara vez menciona Java. Todas sus diapositivas ni siquiera muestran ningún código. Es una architecture pensada para ser aplicada a cualquier proyecto.

¿Por qué estoy tan seguro? Porque he estudiado MVC, MVVM, ReactiveCocoa y Clean Architecture durante 2 años. Me gusta la architecture limpia, la mejor, por mucho. Lo probé convirtiendo 7 proyectos de muestra de Apple en el uso de Clean Architecture. He usado este enfoque exclusivamente durante más de un año. Funciona mejor cada vez.

Algunos de los beneficios son:

  • Encuentra y corrige errores más rápido y más fácil.
  • Extraiga la lógica de negocio de los controlleres de vista en interactores.
  • Extraer la lógica de presentación de los controlleres de vista a los presentadores.
  • Cambie los comportamientos existentes con confianza con testings unitarias rápidas y mantenibles.
  • Escribir methods más cortos con responsabilidad única.
  • Decouple las dependencies de class con límites establecidos claros.

También agregamos un componente de enrutador para que podamos utilizar varios storyboards. No más conflictos.

Las testings de unidad de escritura también se simplifican mucho porque solo necesito probar los methods en los límites. No necesito probar methods privados. Además de eso, ni siquiera necesitaba ningún marco mocking porque escribir tus propios bichos y apodos se vuelve trivial.

Escribí mi experiencia en los últimos 2 años estudiando architecture iOS en Clean Swift . También armé algunas templates Xcode para generar todos los componentes de Arquitectura limpia para ahorrar un montón de time.

ACTUALIZACIÓN – Responder a la pregunta de Víctor Albertos sobre la dependency injection en el comentario a continuación.

Esta es una pregunta realmente grandiosa y exige una respuesta detallada y detallada.

Siempre tenga en count el ciclo VIP . En este caso, el método doSomethingOnLoad() no es un método de límite . Más bien, es un método interno invocado solo en CreateOrderViewController . En las testings unitarias, probamos el comportamiento esperado de una unidad. Damos insumos, observamos resultados, luego comparamos los resultados con nuestras expectativas.

Sí, podría haber hecho doSomethingOnLoad() un método privado. Pero decidí no hacerlo. Uno de los objectives de Swift es facilitar que los desarrolladores escriban código. Todos los methods de límite ya están listdos en los protocolos de input y salida . Realmente no hay necesidad de desperdiciar la class con modificadores privados extraños.

Ahora, necesitamos probar este comportamiento de "The CreateOrderViewController debería hacer algo en carga con esta request de datos" de alguna manera, ¿verdad? ¿Cómo probamos esto si no podemos invocar doSomethingOnLoad() porque es un método privado? viewDidLoad() a viewDidLoad() . El método viewDidLoad() es un método de límite. ¿Qué límite? ¡El límite entre el usuario y el controller de vista! El usuario hizo algo al dispositivo para que cargue otra pantalla. Entonces, ¿cómo invocamos viewDidLoad() entonces? Lo haces así:

 let bundle = NSBundle(forClass: self.dynamicType) let storyboard = UIStoryboard(name: "Main", bundle: bundle) let createOrderViewController = storyboard.instantiateViewControllerWithIdentifier("CreateOrderViewController") as! CreateOrderViewController let view = createOrderViewController.view 

Simplemente invocando la propiedad createOrderViewController.view hará que se viewDidLoad() . Aprendí este truco hace mucho time de alguien. Pero Natasha The Robot también lo mencionó recientemente.

Cuando decidimos qué probar, es muy importante solo probar los methods de límites. Si probamos cada método de una class, las testings se volverán extremadamente frágiles. Cada cambio que hagamos en el código romperá muchas, muchas testings. Mucha gente se rinde debido a esto.

O bien, piénsalo de esta manera. Cuando pregunte cómo CreateOrderRequest , primero pregunte si doSomethingOnLoad() es un método de límite para el que debe escribir la testing. Si no es así, ¿qué es? El método del límite es en realidad viewDidLoad() en este caso. La input es "cuando se carga esta vista". La salida es "llamar a este método con este object de request".

Esta es otra ventaja de usar Clean Swift. Todos sus methods de límite se enumeran en la parte superior del file bajo los protocolos explícitamente nombrados CreateOrderViewControllerInput y CreateOrderViewControllerOutput . ¡No necesitas search en otro lado!

Piensa en lo que sucede si tuvieras que probar doSomethingOnLoad() . Se burla del object de request y luego afirma que es igual a su object de request esperado. Te estás burlando de algo y comparándolo. Es como assert(1, 1) lugar de var a=1; assert(a, 1) var a=1; assert(a, 1) . ¿Cuál es el punto de? Demasiadas testings. Demasiado frágil .

Ahora, hay un momento en el que CreateOrderRequest . Después de que haya verificado que el componente de controller de vista puede generar el CreateOrderRequest correcto. Cuando testing el método del límite CreateOrderInteractor doSomething() CreateOrderRequest , entonces CreateOrderRequest usando la dependency injection de la interfaz.

En resumen, las testings unitarias no se trata de probar cada unidad de una class. Se trata de probar la class como una unidad.

Es un cambio de mentalidad.

¡Espero que ayude!

Tengo 3 series de posts en borrador en WordPress sobre diferentes temas:

  1. Una mirada en profundidad a cada uno de los componentes de Clean Swift
  2. Cómo dividir la compleja lógica comercial en trabajadores y objects de service.
  3. Escritura de testings en la architecture Clean Swift iOS

¿Cuál de estos quieres escuchar primero primero? ¿Debo upload la serie en las testings?

Como respuesta a "cómo crear una aplicación de iOS con múltiples destinos" , la siguiente publicación del blog podría ser útil. Hace poco escribí la publicación para demostrar la configuration de un proyecto con múltiples objectives en la architecture MVVM. Dado que la configuration es demasiado larga para describir aquí, solo le doy el enlace a la página.

https://yoichitgy.github.io/post/dependency-injection-in-mvvm-architecture-with-reactivecocoa-part-2-project-setup/