Migrando seu Completion Handler para Async await

Migrando seu Completion Handler para Async await

Com a evolução do iOS e do Swift ficou muito mais fácil fazer chamadas assíncronas e quando você precisa usar um código legado em uma nova tecnologia?

Códigos legados em swift na maioria das vezes utilizam o Completion Handler para realizar alguma tarefa assíncrona e notificar quem está chamando de que a operação foi realizada e que o e dados está pronto para ser retornado. Observando o código abaixo:

struct Joke: Decodable {
    let icon_url: String
    let value: String
}

func fetchRandomJoke(completion: @escaping (Joke?) -> Void) {
    let url = URL(string: "https://api.chucknorris.io/jokes/random")!

    URLSession.shared.dataTask(with: url) { data, response, error in
        if let data = data {
            if let messages = try? JSONDecoder().decode(Joke.self, from: data) {
                completion(messages)
                return
            }
        }

        completion(nil)

    }.resume()
}

O código acima está escrito da maneira "antiga" de como fazíamos requests em Swift.

Vamos supor que você está desenvolvendo uma nova feature em SwiftUI que vai utilizar a mesma request fecthRandomJoke que outras feature legadas utilizam, você pode acabar com alguns problemas:

  • Async/Await tem alguns comportamentos estranhos rodando com CompletionHandler

  • Pode pensar em criar 2 requests uma nova e outra legada o problema seria se o modelo mudasse com alguma frequência, você teria que alterar em 2 lugares

  • Você pode editar o código legado para usar o Async/Await mas provavelmente teria que mudar em vários lugares porque o código legado estará todo em CompletionHandler

Existe salvação?

Claro! Se você pensou em criar 2 requests você quase acertou o Swift nos permite utilizar uma função chamada withCheckedContinuation para fazermos essa alteração. Veja o código a seguir:

struct Joke: Decodable {
    let icon_url: String
    let value: String
}

func fetchRandomJoke(completion: @escaping (Joke?) -> Void) {
    let url = URL(string: "https://api.chucknorris.io/jokes/random")!

    URLSession.shared.dataTask(with: url) { data, response, error in
        if let data = data {
            if let messages = try? JSONDecoder().decode(Joke.self, from: data) {
                completion(messages)
                return
            }
        }

        completion(nil)

    }.resume()
}

func fetchRandomJoke() async -> Message? {
    await withCheckedContinuation { continuation in
        fetchRandomJoke { joke in
            continuation.resume(returning: joke)
        }
    }
}

let joke = await fetchRandomJoke()
print(joke.value)

Dessa maneira mantemos somente uma request então o problema de alterar em dois lugares foi solucionado e agora você pode utilizar o novo jeito do Swift para lidar com chamadas assíncronas.