Aprende a usar las promesas en Swift con el Framework Combine y el tipo Future
Aprende a usar las promesas en Swift con el Framework Combine y el tipo Future

Domina Promesas en Combine con el Uso de Future

Aprende a crear promesas en Swift con Combine usando Future. Descubre cómo manejar código asíncrono de manera efectiva en tus apps.

SwiftBeta

Tabla de contenido


👇 SÍGUEME PARA APRENDER SWIFTUI, SWIFT, XCODE, etc 👇
Aprende a crear promesas en Swift usando el Framework Combine
Aprende a crear promesas en Swift usando el Framework Combine

Hoy en SwiftBeta vamos a aprender a crear promesas usando el Framework de Apple Combine, y para crearlas vamos a usar un tipo llamado Future, este nuevo tipo llamado Future es muy sencillo de entender, es un Publisher donde podemos publicar un valor o un error, como la mayoría de Publishers que hemos visto durante la serie de Combine, pero este es un poco diferente.

Para darte más contexto de qué es una promesa, no es más que un valor que puede no estar disponible en el momento de crearse pero se espera que esté disponible en algún momento futuro. Es decir, el valor que esperamos si todo va bien lo recibiremos de manera asíncrona, no sabemos cuando, pero sabemos que es en el futuro.

En el video de hoy vamos a ver como podemos crear promesas en Swift usando Future, con varios ejemplos prácticos. Al ser un video nuevo, vamos a crear una nueva page en nuestro Playground. Cada page del Playground es un video que hay en el canal.

Creamos nueva page en el Playground de Xcode

A esta nueva page la vamos a llamar Future, y lo primero de todo que vamos hacer es entender la sintaxis de Future con un metodo. Vamos a crear un método muy sencillo que va a retornar un tipo Future, de esta manera nos podremos suscribir a él.

Empezamos por la firma:

func createUser() -> Future<String, Never> {
...
}
Firma de la función retornando un tipo Future

Esta firma indica que retornamos un tipo Future, que es un Publisher, y que como hemos visto a lo largo de la serie de Combine, el tipo de valor que vamos a recibir va a ser de tipo String, y en este caso nuestro Publisher nunca lanzara un error (por eso el tipo de Failure es Never). Una vez hemos explicado la firma vamos a continuar con la implementacion:

func createUser() -> Future<String, Never> {
	return Future { promise in
    
    }
}
Retornamos un Future

Fijate que al crear la implementacion, ya estamos retornando el Publisher Future y al inicializarlo hay un parámetro que hemos llamado promise (podriamos llamarlo de varias maneras, en mi caso aveces suelo llamarlo callback). Este tipo promise, sera el que usaremos para publicar los valores en nuestro Publisher. Vamos a crear un código asíncrono que se ejecute al cabo de 5 segundos y printe un mensaje por consola:

func createUser() -> Future<String, Never> {
	return Future { promise in
    	DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
        	print("Hello")
        }
    }
}
Código asíncrono printando un valor por consola

Muy sencillo, de momento hemos creado un metodo llamado createUser que retorna un Publisher Future. Ahora, vamos a llamar al método a ver que ocurre:

createUser()
Llamamos a la función createUser()

Al llamar a este método, si esperamos 5 segundos vemos como aparece por consola la palabra "Hello", pero nosotros queremos publicar el String en el Publisher, cómo podemos hacerlo? Usando el parámetro promise de nuestro closure:

func createUser() -> Future<String, Never> {
	return Future { promise in
    	DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
        	promise(.success("Suscríbete a SwiftBeta"))
        }
    }
}
Usamos el parámetro promise para enviar el valor dentro del Publisher

En este caso, a nuestra promesa le hemos pasado el valor "Suscribete a SwiftBeta". Pero podríamos pasarle un error para que lo publicara en el Publisher usando .failure() y el tipo de error.

Vamos a continuar, una vez hemos creado nuestra primera promesa, ahora lo que vamos hacer es suscribirnos. Recuerda que el tipo Future es un Publisher mas y por lo tanto nos podemos suscribir.

Para suscribirnos vamos a usar el método sink, este método lo hemos usado muchas veces a lo largo de toda la serie de Combine:

let cancellable = createUser()
	.sink { value in
    	print("Value received: \(value)")
    }
Retenemos nuestro Subscriber

Si compilamos, al cabo de 5 segundos vemos que aparece por consola el mensaje "Value received: Suscríbete a SwiftBeta". Perfecto, hemos aprendido a crear nuestra primera promesa usando Combine.

Este ejempo que hemos visto es muy sencillo, pero explica claramente como usar Future. Cualquier codigo asíncrono lo puedes empaquetar dentro de un Future, vamos a ver otro ejemplo. Imagina que tienes el siguiente método con un completionBlock, este método lo único que hace es realizar una petición HTTP a la API de Rick & Morty:

struct ResourcesDataModel: Decodable {
    let characters: String
    let locations: String
    let episodes: String
}

func getResources(completionBlock: @escaping  (ResourcesDataModel) -> ()){
    let url = URL(string: "https://rickandmortyapi.com/api")!
    let urlRequest = URLRequest(url: url)
    URLSession.shared.dataTask(with: urlRequest) { data, response, error in
        guard let data else { return }
        let dataModel = try! JSONDecoder().decode(ResourcesDataModel.self, from: data)
        completionBlock(dataModel)
    }.resume()
}
Petición HTTP usando completionBlock

Dentro de este método hacemos una petición HTTP. Si queremos ejecutar el método para realizar la petición HTTP, vamos a llamar a nuestro método:

getResources { dataModel in
    print("Resultado \(dataModel.characters)")
}
Llamamos al método y obtenemos el resultado de la petición HTTP

Si compilamos nuestro Playground, vemos como se printa por consola el resultado con la respuesta correcta de haber hecho la petición HTTP. Esto está muy bien, pero cómo podemos transformar este método para que use promesas con el tipo Future que hemos visto en el video de hoy? Pues la verdad es que es muy sencillo, creamos un nuevo método llamado getResourcesPromise y vamos a empaquetar esta lógica dentro de nuestra promesa:

func getResourcesPromise() -> Future<ResourcesDataModel, Never> {
    return Future { promise in
        let url = URL(string: "https://rickandmortyapi.com/api")!
        let urlRequest = URLRequest(url: url)
        URLSession.shared.dataTask(with: urlRequest) { data, response, error in
            guard let data else { return }
            let dataModel = try! JSONDecoder().decode(ResourcesDataModel.self, from: data)
            promise(.success(dataModel))
        }.resume()
    }
}
Creamos la misma lógica pero usando una Promesa de Combine

Al inicio del video mencionábamos que una promesa puede enviar un valor de forma asíncrona, y en este caso es cuando obtenemos la respuesta al realizar la petición HTTP. En este escenario publicamos el valor una vez hemos parseado correctamente data a nuestro modelo de dominio. Y en este caso no estamos teniendo en cuenta ningún error, pero lo podríamos controlar y en caso de detectar un error podríamos enviarlo a través de nuestra promesa usando promise(.failure(y aquí el tipo del error))

Una vez hemos creado nuestra promesa, vamos a ejecutarla. Utilizamos el método sink para suscribirnos a nuestro Publisher:

let cancellable = getResourcesPromise()
    .sink { value in
        print("Value \(value.characters)")
    }
El subscriber obtiene el dataModel obtenido al hacer la petición HTTP

Si ahora compilamos vemos recibimos el valor de value.

Conclusión

Hoy en SwiftBeta hemos aprendido a cómo crear una promesa usando el framework Combine. Al inicio del video hemos visto un ejemplo muy sencillo usando DispatchQueue, y hemos continuado con un ejemplo más real usando URLSession.

Y hasta aquí el video de hoy!