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()
}
By the way, if you frequently work with closures, there is an approach that can help improve the efficiency and safety of this technique. You can learn more about it here.
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.