Describe the process of converting a synchronous API call into an asynchronous one in Swift.
Instruction: Provide an example of transforming a synchronous function into an asynchronous one using Swift’s concurrency features.
Updated
Official answer
If a library offers a native asynchronous operation, use it instead of merely adding async to a blocking function. For network loading, replace a synchronous Data(contentsOf:) call with URLSession's asynchronous API. The following complete definitions target iOS 15 or later and propagate transport, response and decoding errors:
import Foundation
struct UserData: Decodable, Sendable { let name: String }
enum FetchError: Error {
case invalidURL
case invalidResponse
case httpStatus(Int)
}
func fetchUserData(
from url: URL, session: URLSession = .shared
) async throws -> UserData {
guard url.scheme?.lowercased() == "https", url.host != nil else {
throw FetchError.invalidURL
}
try Task.checkCancellation()
let (data, response) = try await session.data(for: URLRequest(url: url))
guard let http = response as? HTTPURLResponse else {
throw FetchError.invalidResponse
}
guard (200..<300).contains(http.statusCode) else {
throw FetchError.httpStatus(http.statusCode)
}
try Task.checkCancellation()
return try JSONDecoder().decode(UserData.self, from: data)
}
Call this from an async context with try await. A MainActor-isolated screen model should own its loading/error/success state and task lifetime. Network suspension lets other work proceed; decoding is synchronous work after the await, so bound response size and deliberately isolate expensive parsing if measurement requires it. Task creation does not itself guarantee background execution or a main-actor UI update.
For a callback-based API, a checked continuation can bridge it, but every path must resume exactly once and cancellation needs a separate design. For a truly blocking legacy API with no async equivalent, use a controlled blocking-work facility and a continuation only when necessary; a continuation alone does not move work off the calling executor. Avoid blocking cooperative executor threads or creating unbounded detached work.
Test successful JSON, non-HTTP responses, HTTP errors with otherwise valid JSON, malformed payloads and cancellation. HTTPS validation here is not a hostname allowlist or an authorization policy, and the example does not implement retries or streaming.