Async/await in Swift


Greetings, traveler!

Async/await is a new approach to multithreading in Swift. It simplifies writing complex call chains and makes the code readable. Async/await is part of Swift Concurrency, allowing multiple code pieces to run simultaneously.

To begin with, let’s examine an example of code that we can modify using Swift Concurrency. We will create a function to upload images from the Internet. This function returns the result of the download operation in a closure, which can be either successful or unsuccessful. One drawback of this solution is that we must remember to call this closure at the end of each scenario we process.

enum ImageError: Error {
    case invalidData
}

func loadImage(
    url: URL,
    _ completion: ((Result<UIImage, Error>) -> Void)?
) {
    
    let task = URLSession.shared.dataTask(with: url) { data, _, error in
        guard let data, let image = UIImage(data: data) else {
             completion?(.failure(error ?? ImageError.invalidData))
            return
        }
        
        completion?(.success(image))
    }
    
    task.resume()
}

Let’s rewrite the code using async/await. We will mark the function with the ‘async’ keyword. But not just async, but ‘async throws’ to give the function the ability to throw an error.

func loadImage(url: URL) async throws -> UIImage {
    let request = URLRequest(url: url)
    
    let (data, _) = try await URLSession.shared.data(for: request)
    
    guard let image = UIImage(data: data) else {
        throw ImageError.invalidData
    }
    
    return image
}

To call this function, we must wrap it into a Task and use the ‘await’ keyword.

Task {
    do {
        let image = try await loadImage(url: URL(string: "https://picsum.photos/200/300")!)
    } catch {
        print(error)
    }
}

In the following article, we will discuss Tasks in more detail. See you there.