Después de haber estado dandole vueltas a esto, he hecho lo siguiente.

El objetivo era conseguir esta interfaz:


useCase.execute(request).then { response in
    print("Received: \(response)")
}


Bien, el problema que me encontré eran las llamadas seguidas al mismo caso de uso.

useCase.execute().then { response in
    print("Received first call \(response)")
}

useCase.execute().then { response in
    print("Received second call \(response)")
}

Como el caso de uso guardaba los bloques then y catch, había hecho un clean en el execute, de este modo, cuando se le volvía a llamar limpiaba los antiguos bloques.

¿Veis el problema? Cuando se llama dos veces seguidas, limpia los primeros, pero no la ejecución, entonces cuando nuestro trabajo asíncrono termina, llama a los segundos bloques con el primer resultado, y vuelve a llamarlos con el segundo.

La primera solución que se me ocurrió es hacer algo que fuese clonable y devolver la copia. De ese modo, no habría ningún problema. Pero, de verdad por cada caso de uso que implemente, tengo que hacer un clone? Eso no me gustaba nada. 🤢


The history

Fui a una de las charlas de las NSCoders, estando un rato hablando con Jorge Ortiz sobre inyección de dependencias, llegamos a hablar de los casos de uso. Él me contaba que creaba los casos de uso bajo demanda, de este modo, puede tener una pool o algo así de casos de uso ejecutados e implementar un deshacer, de una forma limpia y mas fácil que si tenemos la instancia y esta hace varias ejecuciones.

¿Cómo guardaríamos así las distintas operaciones que hacemos?

En ese momento, no le di mucha importancia, el tema principal era la inyección de dependencias y el framework en concreto del que hablábamos. Pero unos días después, cuando volvía a pensar sobre el caso de uso, sí le di mas importancia…

¿Como guardar las operaciones hechas para poder hacer un deshacer?

Empecé a notar cierta relación entre esa pregunta y esta, ¿Cómo consigo que mi caso de uso no tenga el problema de varias llamadas seguidas?

Jorge me hablaba de el uso de factorías para crear casos de uso. Keep in mind Factoría.

Hace cosa de un año, recuerdo haber leído algo sobre casos de uso bajo operaciones en el blog de ¿gitdo?, o algo así, el autor era Pedro Piñera, pero no he encontrado el blog ni la app ni nada… 😞

Entonces, empecemos a juntar piezas.

  1. El caso de uso es una factoría, pero claro, no una factoría de casos de uso, el no se crea a si mismo, ni se duplica ni nada…
  2. La ejecución correrá en segundo plano y además usando el framework de Operation de 
  3. Mantendrá la interfaz que conseguí en el post original
  4. Opción a implementar un Pool de ejecuciones

UseCase

Con todo lo anterior en la cabeza, llegué a la siguiente conclusión. Un caso de uso base.

La utilidad de este no es otra que crear una operación cuando se llama al execute y pasarla a una OperationQueue.

Encuentro bastante sentido a crear la operación. Pues la operación representa la ejecución de nuestro código, una y solo una vez. Almacena en sí todos sus thens y catchs. También la request que recibe y su respuesta o error. Controlamos la ejecución de ésta mediante la cola, pues podemos definir el numero de operaciones concurrentes, pararlas o incluso ir cancelando las anteriores según añadimos nuevas (muy útil en las búsquedas por texto, por ejemplo, así cancelamos las anteriores cada vez que cambia el string que buscamos).

El método que sobre escribimos, es un método main. Recibimos una Request opcional, no todos nuestros casos de uso necesitan datos obligatoriamente. Sin embargo, siempre devolvemos una Response.

De este modo, nuestro caso de uso base, puede ser extendido con una subclase, que implemente todo el rollo del pool de operaciones. De hecho, la operación base, también puede ser extendida para tener un método undo. De modo que podemos deshacer la operación, mover el puntero que usemos en el pool de operaciones. Podemos rehacer, y en caso de llegar una nueva acción, borrar todas las que estén por delante del puntero.

Momento publicitario 😝

Pues como me gustó como quedó, decidí pasar unos test para asegurarme de que se ejecutaba todo como quería. Y solo por curiosidad, creé un pod. El pod solo nos provee de la clase base del UseCase<Request, Response>, con su método execute. Hacer funcionar nuestros casos de uso con este mecanismo no es mas que escribir el código dentro del main, en una subclase.

Puedes instalarlo y probar como funciona añadiendo pod 'UseCase' a tu Podfile.

Y como siempre, si se os ocurre un modo de mejorarlo, encontráis un bug o simplemente os apetece ponerme a parir. Aquí mi twitter, y en github está la zona de issues para cualquier cosilla!