Publishers Combine: PassthroughSubject y CurrentValueSubject
PassthroughSubject y CurrentValueSubject son dos publishers que nos permiten publicar eventos en un Publisher de forma imperativa. Usamos un método llamado send para enviar los eventos. La diferencia entre PassthroughSubject y CurrentValueSubject es que al último le damos un valor por defecto
Tabla de contenido
Hoy en SwiftBeta vamos a aprende sobre dos Publishers que podemos usar con el framework Combine. Son PassthroughSubject y CurrentValueSubject, al usarlos podemos enviar valores a los subscribers de forma imperativa, es decir, podemos usar un método llamado send para publicar los valores en nuestro Publisher. Y la única diferencia entre estos dos Publishers, es que con CurrentValueSubject podemos darle un valor por defecto, es decir, cuando lo inicializamos le damos un valor que más tarde cuando un subscriber se suscriba lo recibirá automáticamente. En cambio, PassthroughSubject lo inicializamos sin ningún valor, y por lo tanto después de suscribirnos deberemos enviar algún valor para que el subscriber reciba valores. Y en ambos Publishers usamos el método send para enviar valores en nuestra pipeline.
Hoy vamos a ver ejemplos muy prácticos y vamos a crear una nueva page en nuestro Playground. Vamos a simular que tenemos una app del pronóstico del tiempo que nos va publicando temperaturas en un Publisher y para hacerlo vamos a crear un PassthroughSubject.
Pero antes, si quieres apoyar el contenido que subo cada semana, suscríbete. De esta manera seguiré publicando contenido completamente grautito en Youtube.
PasstroughSubject
Para hacerlo, voy a crear una struct llamada Weather, voy a poner el modo rápido y luego entraré en detalle en cada parte del código:
Para crear nuestro PassthroughSubject, fíjaque que hemos creado una propiedad. En esta propiedad hemos especificado el Output y el Failure. El Output es el tipo Int, que representará las temperaturas que enviaremos en nuestro Publisher. Y el Failure es el tipo Error, ya que en este caso podremos enviar errores a través de nuestro Publisher.
A parte de la propiedad, hemos creado un método llamado getWeatherInfo que dentro usa el método send, este método lo usamos para enviar los valores de tipo Int que se enviarán para que el subscriber pueda recuperar esos valores, esas temperaturas.
Ahora, vamos a usar el método sink para suscribirnos y poder recoger los valores que envía el publisher:
Si compilamos nuestro Playground, aparece por consola el siguiente resultado:
Pero, como te decía hace un rato, no solo podemos enviar enteros, también como vimos en el anterior video, una vez hemos enviado todos los valores en nuestra pipeline, podemos enviar un valor a través de la pipeline que indique al subscriber que hemos finalizado.
Y es curioso, que después de enviar que nuestro Publisher ha finalizado, cualquier otro valor que enviemos, nunca se enviará en nuestro Publisher y por lo tanto el Subscriber no lo recibirá. Vamos a verlo en un ejemplo:
Para indicar que el Publisher ha finalizado de enviar valores, hemos usado un enum que su case es .finished. Si compilamos, vamos a ver qué ocurre:
Pero, imagina que en lugar de indicar que hemos finalizado de publicar todos los valores, imagina que hemos obtenido un error, por el motivo que sea, en este caso, en lugar de enviar un .finished, podríamos enviar un error, ya verás que sencillo es, acontinuación vamos a enviar un error ya definido en la librería standard de swift:
Al compilar obtenemos el siguiente mensaje por consola:
Poco a poco ya vamos entendiendo cada vez más como funciona el framework Combine. Pero quizás te preguntas ¿Podríamos diferenciar en el método sink cuando el publisher finaliza correctamente o obtenemos un error? Y la respuesta es que sí, tan solo necesitamos usar un switch:
Al hacerlo de esta manera podemos separar el flujo de nuestra app y realizar una lógica si hemos completado correctamente o por lo contrario hemos obtenido un error.
Al usar PassthroughtSubject podemos enviar eventos según nuestra necesidad, es decir, podemos enviar un valor o especificar un completado o error dentro de nuestro Publisher. Nos da mucha flexibilidad. Ahora vamos a ver qué diferencia hay con CurrentValueSubject y vamos a introducir más conceptos interesantes de combine.
CurrentValueSubject
Ahora, en lugar de crear un ejemplo sobre el pronóstico del tiempo, vamos a crear un ejemplo que simule que hemos creado una app de chat. Imagina, te bajas telegram o whatsapp, y nada más hacerlo quieres que se te cree una conversación vacía de un bot explicándote el onboarding de la app. El onboarding es un tutorial con mensajes que vas recibiendo en una conversación, te va orientando y te va dando consejos de cómo usar la app, por ejemplo nada más instalar la app recibes un mensaje: Primer mensaje, crea una conversación con algún contacto. Al cabo de X segundos de usarla recibes otro mensaje, este segundo mensaje podría ser: Envía un sticker, etc Vamos a simular este comportamiento en una struct llamada BotApp:
Vamos hacer exactamente lo mismo que hemos visto en el ejemplo anterior con PassthroughSubject, pero esta vez la propiedad será de tipo CurrentValueSubject. En este caso, nuestra propiedad CurrentValueSubject ya tiene un valor por defecto de "Bienvenido a SwiftBeta", que se recibirá una vez nos suscribamos a este publisher. Y en este caso, el Output de nuestro Publisher es de tipo String, y el error es de tipo Error. Ahora, vamos a suscribirnos a nuestro Publisher:
Si ahora compilamos, el resultado que obtenemos es:
Perfecto, ahora vamos a simular que pasan 2 segundos y 6 segundos para enviar más valores en nuestro publisher llamado onboardingPublisher. Para hacerlo podemos usar DispatchQueue:
Con el anterior código vamos a publicar 3 valores a nuestro onboardingPublisher. Vamos a compilar para ver qué ocurre. SPOILER: No vamos a recibir los valores que tenemos dentro de los DispatchQueue
Al compilar seguimos recibiendo estos valores por consola:
Algo está ocurriendo, podemos debuggar nuestro publisher usando un método llamado handleEvents. Con este método podemos saber qué ocurre exactamente en nuestro publisher. Y para usarlo, ponemos el siguiente código:
Todos los closures del método handleEvents son opcionales, es por eso que podemos omitir y borrar el que queramos.
He comentado algunas líneas del método sink para que sea más fácil de debuggar. Si compilamos, obtenemos el siguiente resultado por consola:
La información que obtenemos es muy valiosa, y con el último mensaje podemos hacernos una idea de que está ocurriendo, el que aparece Cancel Received, algo está ocurriendo que nuestro subscriber ha dejado de escuchar lo que envía el Publisher. En el anterior video, te comentaba que lo publishers tienen un ciclo de vida, en nuestro ejemplo, no hay nada que esté reteniendo a nuestro publisher en memoria, y por lo tanto se acaba liberando.
Para evitar que se libere podemos guardar una referencia en el tipo AnyCancellable. Y es tan sencillo como hacer lo siguiente:
Al usar la constante cancellable, si ejecutamos nuestro código en el playground, observamos que vamos obteniendo los valores que se publican en nuestroi publisher:
Ahora que ya entendemos lo que ocurre, podemos dejar de usar el método handleEvents y quedarnos solo con el método sink:
vamos a compilar y vamos a ver el resultado que aparece por consola:
Conclusión
Hoy hemos aprendido a usar dos publishers en los que podemos enviar valores cuando queramos. Estos publishers tienen un método llamado send en el que podemos enviar valores, errores o indicar que el publisher ha completado de enviar eventos.
También hemos aprendido la diferencia entre PassthroughtSubjet y CurrentValueSubject. Su difierencia es que con CurrentValueSubject podemos asignar un valor por defecto al Publisher para que cuando un subscriber se suscriba, reciba ese valor sin tener que ser enviado explicitamente por el publisher.
Y hasta aquí el video de hoy!