How to refactor Swift API call to use Swift 5.5 async/await?

Viewed 293

I want to refactor some API calls to use Swift 5.5's new async/await in my SwiftUI project. However, it's unclear to me how to replace or accomodate the completions.

Here's an example function which I want to refactor:

 static func getBooks(completion: @escaping ([Book]?) -> Void) {
    let request = getRequest(suffix: "books")
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            fatalError("Error: \(error)")
        }
        if let data = data {
            if let books = try? JSONDecoder().decode([Book].self, from: data) {
                DispatchQueue.main.async {
                    print("books.count: \(books.count)")
                    completion(books)
                }
                return
            } else {
                fatalError("Unable to decode JSON")
            }
        } else {
            fatalError("Data is nil")
        }
    }.resume()
}

I beleve the new function signature would look something like this:

static func getBooks() async throws -> ([Book]?) {
   // ...
}

However, I have no idea what to do with the URLSession.shared.dataTask, DispatchQueue.main.async and completion, etc.

Anyone know what the new function body should look like?

Thanks

1 Answers
func getBooks() async throws -> [Book] {
    let (data, _) = try await URLSession.shared.data(for: request)
    return try JSONDecoder().decode([Book].self, from: data)
}

This will throw if the request fails, and if the response cannot be decoded. Since the function is marked as throwing, then the calling function has to handle the raised errors.

You don't need to declare the returned [Book] to be optional, because it will either return an honest array, or throw an error.

In your additional code, you had to call your completion handler on the main queue, because you were calling it from within the completion block of the request. You don't need to do that here.

Related