Agrupa varios Publishers en 1 usando CombineLatest y Zip de Combine
Agrupa varios Publishers en 1 usando CombineLatest y Zip de Combine

Agrupa Publishers con CombineLatest y Zip en Combine

CombineLatest y Zip son dos operadores de Combine que nos permiten agrupar varios Publishers en uno. La ventaja de usar estos operadores es que nos ayudan a reducir mucha lógica dentro de nuestra aplicación. En lugar de crear esta funcionalidad nosotros, podemos usar el operador.

SwiftBeta

Tabla de contenido


👇 SÍGUEME PARA APRENDER SWIFTUI, SWIFT, XCODE, etc 👇
CombineLatest y Zip en Combine en Swift
CombineLatest y Zip en Combine en Swift

Hoy en SwiftBeta vamos a ver a cómo agrupar varios Publishers usando el operador CombineLatest y Zip, en la práctica son tremendamente útiles, y vamos a ver por qué. CombineLatest sirve para agrupar varios publishers y como resultado produce un nuevo Publisher. La magia de este operador es que escucha los valores emitidos por cada Publisher, y cuando recibe un valor de cada publisher, publica una tupla con los valores obtenidos.

Hay muchos ejemplos donde puedes usar CombineLatest, por ejemplo, imagina que dentro de tu aplicación tienes una pantalla donde debes realizar varias peticiones HTTP, por ejemplo el perfil de un usuario. Cada petición HTTP sirve para mostrar diferentes secciones del perfil. Por ejemplo, en una sección puedes mostrar la información del usuario (imagen, nombre, apellidos, etc), en otra sección puedes mostrar información de pago (tarjetas de crédito, paypal, etc), y en otra sección puedes mostrar los datos de envío (país, provincia, calle, etc). Al entrar a la pantalla puedes realizar las 3 peticiones HTTP y usar CombineLatest para esperar a que se completen, de esta manera, al obtener los datos de las 3 peticiones puedes actualizar la pantalla de perfil de usuario de golpe.

Vamos a ver un ejemplo muy práctico para entender cómo funciona CombineLatest

Creamos una nueva Page en el Playground

Lo primero de todo que vamos hacer es crear una nueva Page en nuestro Playground, la vamos a llamar CombineLatest y Zip. Al crear la nueva page lo siguiente que vamos hacer es importar el framework Combine

import Combine
Importamos Combine

En este ejemplo práctico, vamos a crear 2 publisher para poder emitir valores de tipo Int. Para que sea posible, vamos a crear 2 publishers de tipo PassthroughSubject (este tipo de Publisher ya lo hemos visto en el canal, si no sabes de él te lo dejo por aquí arriba para que le eches un vistazo):

let numberPublisher1 = PassthroughSubject<Int, Never>()
let numberPublisher2 = PassthroughSubject<Int, Never>()
Creamos 2 Publishers de tipo PasstroughSubject

Muy sencillo, hemos creado 2 Publishers que pueden publicar valores de tipo Int, y no puedes publicar errores, por eso el tipo de nuestros Publisher es Int,Never.

Ahora, antes de continuar, si quieres apoyar el contenido que subo cada semana, suscríbete. De esta manera seguiré publicando contenido completamente grautito en Youtube.

CombineLatest

Vamos a seguir, una vez tenemos los 2 Publishers, ahora vamos a usar CombineLatest para agruparlos en uno.

Publishers.CombineLatest(numberPublisher1, numberPublisher2)
Agrupamos 2 Publishers en 1

Perfecto, una vez usamos CombineLatest, podemos usar map. Al usar map recibiremos el valor publicado por numberPublisher1 y numberPublisher2.

Publishers.CombineLatest(numberPublisher1, numberPublisher2)
    .map { number1, number2 -> Int in
        return number1 + number2
    }
Utilizamos operador map

Como mencionaba al inicio del video, lo que hacemos con CombineLatest es agrupar 2 Publishers en 1. Por lo tanto, podemos usar el método sink para suscribirnos a él:

let subscription = Publishers.CombineLatest(numberPublisher1, numberPublisher2)
    .map { number1, number2 -> Int in
        return number1 + number2
    }
    .sink { sum in
        print("La suma de los números es: \(sum)")
    }
Utilizamos operador sink para suscribirnos al Publisher

Una vez hemos agrupado los 2 Publishers en uno, ahora solo nos queda publicar valores en numberPublisher1 y numberPublisher2, ¿cómo lo hacemos? Muy sencillo, usando el método send que vimos en los anteriores videos.

Vamos a publicar un Int en nuestro primer Publisher, para ver qué ocurre.

numberPublisher1.send(3)
Publicamos 1 valor en el Publisher1

Si compilamos nuestro Playgrounds, vemos que no ocurre nada. Es decir, no se ejecuta el contenido del map. Esto es debido a lo que te comentaba. Para que se ejecute el map, primero CombineLatest debe recibir un valor de cada Publisher. Es decir, debemos publicar un valor en nuestro numberPublisher2

numberPublisher2.send(5)
Publicamos 1 valor en el Publisher2

Al hacerlo, vemos que automáticamente aparece por consola:

La suma de los números es: 8
Obtenemos la suma de los 2 valores

Justo el resultado que esperábamos, hemos publicado un valor en cada Publisher, y por lo tanto el closure del map se ha ejecutado, realizando la suma.

Vamos a investigar un poco más, una vez hemos publicado un valor en cada Publisher, si ahora volvemos a publicar un valor en el Publisher 2 vamos a ver qué ocurre:

numberPublisher2.send(20)
Publicamos 1 valor en el Publisher2

Muy interesante! se printa por consola el sigueinte mensaje:

La suma de los números es: 8
La suma de los números es: 23
Obtenemos la suma de los 2 valores

Si te fijas, primero realiza la suma con el valor 3 y 5, y luego realiza la suma con el valor 3 y 20, dando como resultado 23. En este caso ha usado el valor 3 del Publisher 1 ya que era el último valor que se había publicado, y ha usado el valor 20 en el Publisher 2 ya que era el último valor publicado. Tiene sentido.

Como curiosidad, en este caso, estamos agrupando 2 Publishers, pero podríamos agrupar varios más. Si vas a la documentación, aquí podemos ver CombineLatest3, CombineLatest4, etc esto indica el número de parámetros que le podemos pasar a CombineLatest.

Muy bien, una vez hemos aprendido cómo agrupar varios Publishers con CombineLatest, ahora vamos a aprender a cómo usar Zip.

Zip

El uso de Zip es muy parecido al de CombineLatest, la única diferencia es que Zip espera a que se publique siempre un nuevo valor en los Publishers que se están agrupando. Es decir, en el caso de CombineLatest, cuando hemos publicado el segundo valor en el Publisher 2, el map se ha ejecutado y ha usado como primer parámetro el valor 3 y luego el valor 20.
En este caso, el Zip no se ejecutaría hasta que el Publisher 1 no hubiera publicado un nuevo valor. Es decir, para que el Zip publique un nuevo valor, se tiene que publicar un valor en el Publisher 1 y en el Publisher 2 (siempre tienen que ir a la par).

Vamos a ver un ejemplo y lo vamos a ver muy claro. Vamos a modificar el código que hemos creado para entender cómo funciona Zip. Para hacerlo tan solo debemos reemplazar CombineLatest por Zip:

let numberPublisher1 = PassthroughSubject<Int, Never>()
let numberPublisher2 = PassthroughSubject<Int, Never>()

let subscription = Publishers.Zip(numberPublisher1, numberPublisher2)
    .map { number1, number2 -> Int in
        return number1 + number2
    }
    .sink { sum in
        print("La suma de los números es: \(sum)")
    }

numberPublisher1.send(3)
numberPublisher2.send(5)
Sustituimos CombineLatest por Zip

Si compilamos, a simple vista es exactamente igual al CombineLatest, el resultado que se muestra es 8:

La suma de los números es: 8
Obtenemos el mismo resultado que con combineLatest

Pero! si ahora publicamos un valor en el Publisher 2, vamos a ver qué ocurre:

numberPublisher2.send(20)
Pubicamos 1 valor en el Publisher2

Si compilamos, vemos que no aparece nada por consola, no se está triggereando el closure del map, solo se está haciendo la primera suma cuando se publica un evento en cada Publisher:

La suma de los números es: 8
No ocurre nada

Es decir, aunque hayamos publicado un nuevo valor en el Publisher 2, con el valor de 20, no está ocurriendo absolutamente nada, Zip no ha publicado la tupla, y por lo tanto no se ha ejecutado el map para realizar la suma. Para que el Zip publique la tupla tenemos que publicar un nuevo valor en el Publisher 1. Es decir, a parte de Publicar el valor 20 en el Publisher 2, ahora vamos a publicar un 10 en el Publisher 1:

numberPublisher1.send(10)
numberPublisher2.send(20)
Publicamos 1 valor en el Publisher1 y otro valor en el Publisher2

Al hacerlo vemos que se printa otro mensaje por consola con el valor de la suma:

La suma de los números es: 8
La suma de los números es: 30
Ahora sí, obtenemos el resultado de la suma

Fíjate que esto es muy potente, en este caso hemos visto CombineLatest y Zip y cada operador tiene su propia lógica interna y depende de nosotros cuando utilizar uno u otro, es por eso que es importante conocer todos estos operadores de Combine, para saber exactamente cuándo utilizar uno u otro y así nosotros no implementar lógica que ya viene dada en un operador.

Conclusión

Hoy hemos aprendido a agrupar varios Publishers en uno. Hemos aprendido a usar CombineLatest y Zip de Combine. Que sepas que hay muchos más, y aprenderlos te ayudará mucho a crear implementaciones muy limpias dentro de tu aplicación.