Skip to main content

Command Palette

Search for a command to run...

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?

Updated
•2 min read•View as Markdown
Migrando seu Completion Handler para Async await

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.