Returning data from async call in Swift function

Introduced in Swift 5.5 (iOS 15, macOS 12), we would now use the asyncawait pattern:

func fetchGenres() async throws -> [Genre] {
    …
    let (data, _) = try await URLSession.shared.dataTask(for: request)
    return try JSONDecoder().decode([Genre].self, from: data)
}

And we would call it like:

let genres = try await fetchGenres()

The asyncawait syntax is far more concise and natural than the traditional completion handler pattern outlined in my original answer, below.

For more information, see Meet async/await in Swift.


The historic pattern is to use completion handlers closure.

For example, we would often use Result:

func fetchGenres(completion: @escaping (Result<[Genre], Error>) -> Void) {
    ...
    URLSession.shared.dataTask(with: request) { data, _, error in 
        if let error = error {
            DispatchQueue.main.async {
                completion(.failure(error))
            }
            return
        }

        // parse response here

        let results = ...
        DispatchQueue.main.async {
            completion(.success(results))
        }
    }.resume()
}

And you’d call it like so:

fetchGenres { results in
    switch results {
    case .failure(let error):
        print(error.localizedDescription)

    case .success(let genres):
        // use `genres` here, e.g. update model and UI            
    }
}

// but don’t try to use `genres` here, as the above runs asynchronously

Note, above I’m dispatching the completion handler back to the main queue to simplify model and UI updates. Some developers take exception to this practice and either use whatever queue URLSession used or use their own queue (requiring the caller to manually synchronize the results themselves).

But that’s not material here. The key issue is the use of completion handler to specify the block of code to be run when the asynchronous request is done.


Note, above I retired the use of NSArray (we don’t use those bridged Objective-C types any more). I assume that we had a Genre type and we presumably used JSONDecoder, rather than JSONSerialization, to decode it. But this question didn’t have enough information about the underlying JSON to get into the details here, so I omitted that to avoid clouding the core issue, the use of closures as completion handlers.

Leave a Comment