Operador tryMap en el framework Combine de Swift
Operador tryMap en el framework Combine de Swift

Operador Combine: tryMap

tryMap es un operador en Combine que nos permite transformar de un tipo A a un tipo B según la función que le pasemos. La única diferencia respecto al operador map de Combine, es que tryMap permite lanzar errores a nuestro Publisher y impedir que se sigan publicando valores.

SwiftBeta

Tabla de contenido


👇 SÍGUEME PARA APRENDER SWIFTUI, SWIFT, XCODE, etc 👇
Aprende a usar el operador tryMap del framework Combine de Apple en Swift
Aprende a usar el operador tryMap del framework Combine de Apple en Swift

Hoy en SwiftBeta vamos a aprender a usar tryMap, otro operador de Combine, este operador es muy parecido al operador map que vimos en el anterior video, es decir, lo usamos para transformar un tipo A que viaja en nuestro Publisher a un tipo B y la diferencia con el operador Map es que tryMap nos permite parar de publicar valores cuando obtenemos un error ocasionado dentro del map, el error que ocurre dentro del map viaja por todo el stream hasta llegar al suscriber. Lanzar errores dentro de tryMap es posible ya que su firma tiene el throw, el cual nos permite lanzar errores.

Y ¿cómo puede pasar esto? Imagina que tienes que desarrollar un Publisher que publique valores de tipo Strings, y por cada String que envíes, debes transformarlo a Int. Habrá casos en que se pueda transforma la String a Int, y otros casos en que no. Por ejemplo, si recibes un "32", podrás transformar fácilmente a Int y sería 32.  Pero si quieres transformar "SwiftBeta" a Int, no podrías, ya que "SwiftBeta" no se puede representar como Int, y por lo tanto en este caso lanzarías un error. Al lanzar el error dentro del operador tryMap, se envía un failure en el Publisher que hace que ya no se puedan publicar más valores. Es decir, nuestra pipeline no puede enviar más valores para que el subscriber los recupere.

El ejemplo que te acabo de explicar, lo vamos a representar en nuestro Publisher usando el operador tryMap.

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

Operador tryMap

Lo primero de todo que vamos hacer es crear nuestro PassthroughSubject de tipo String. Y en este caso necesitamos poder enviar errores a través de nuestro Publisher, ya que dentro del tryMap puede que se lance algún error y esto se convierta en enviar un Failure a través de nuestro Publisher. Pues lo primero de todo que vamos hacer es crear un enum y lo vamos a llamar SwiftBetaError con un único case:

enum SwiftBetaError: Error {
    case errorStringToInt
}

let stringPublisher = PassthroughSubject<String, SwiftBetaError>()
Creamos un tipo de error para enviar en nuestro Publisher

Una vez hemos creado nuestro Publisher indicando qué valores se van a publicar y el tipo de error. Vamos a continuar, ahora vamos a suscribirnos a nuestro Publisher, y vamos a publicar valores usando el método send:

enum SwiftBetaError: Error {
    case errorStringToInt
}

let stringPublisher = PassthroughSubject<String, SwiftBetaError>()

stringPublisher
     .sink(receiveCompletion: { finished in
         print("Completed \(finished)")
     }, receiveValue: { value in
         print("Value \(value)")
     })

stringPublisher.send("35")
stringPublisher.send("32")
Nos suscribimos al Publisher y enviamos varios valores

Si compilamos, obtenemos el siguiente resultado (de tipo String, ya que aún no hemos hecho nada para transformar los valores que viajan por nuestro Publisher a Int):

Value "35"
Value "32"
Los valores enviados son los que recibimos en el subscriber

Hasta aquí todo bien, ahora vamos a usar el operador tryMap. Pero para usarlo debemos crear una función que se encargue de transformar de String a Int. En caso de poder transformar de String a Int, la función devuelve el Int. Y en caso de no poder transformar de String a Int se lanza un error de tipo SwiftBetaError (el enum que hemos creado hace un momento):

enum SwiftBetaError: Error {
    case errorStringToInt
}

func mapStringToInt(with stringValue: String) throws -> Int {
    guard let result = Int(stringValue) else {
        throw SwiftBetaError.errorStringToInt
    }
    return result
}

let stringPublisher = PassthroughSubject<String, SwiftBetaError>()

stringPublisher
     .tryMap { try mapStringToInt(with: $0) }
     .sink(receiveCompletion: { finished in
         print("Completed \(finished)")
     }, receiveValue: { integer in
         print("Value \(integer)")
     })

stringPublisher.send("35")
stringPublisher.send("32")
Creamos una función y usamos tryMap para transforma un valor String a Int

Si volvemos a compilar, obtenemos el mismo resultado pero esta vez los valores que hemos enviado como String, ahora son Int. Se transforma correctamente del tipo String a Int.

Pero si envíamos en nuestro Publisher una String que no se puede transformar en Int, como por ejemplo:

stringPublisher.send("35")
stringPublisher.send("SwiftBeta")
stringPublisher.send("32")
Enviamos el valor "SwiftBeta" en nuestro Publisher

¿Qué ocurre?

Value 35
Completed failure(__lldb_expr_34.SwiftBetaError.errorStringToInt)
"SwiftBeta" no se puede representar como Int, es por eso que se lanza un error en nuestro Publisher

Y esto es exactamente lo que hace el operador tryMap, si lanza un error dentro de la función del map, hace que el Publisher envíe automáticamente el error por la pipeline completando el publisher y por lo tanto impidiendo que se puedan enviar más eventos. Fíjate que aunque estemos enviando la String "32", la pipeline ya no envía más valores.

Este operador es muy útil para poder lanzar errores desde nuestro map. De esta manera nuestro Publisher deja de seguir enviando eventos. Muy interesante, acabamos de aprender otro operador que podemos usar en nuestros Publishers.

Conclusión

Hoy en SwiftBeta hemos aprendido a usar otro operador muy útil en Combine llamado tryMap. Este operador permite lanzar un error a nuestro Publisher y por lo tanto impedir que se puedan publicar más valores en él.

Y hasta aquí el video de hoy.