Plenty of frameworks still hand you a completion-handler API with no async overload — CoreLocation delegates, third-party SDKs, legacy internal networking layers. Rewriting them isn't always an option, but calling them from async code still needs to work.
#The old way
func requestReview(for orderID: String, completion: @escaping (Bool) -> Void) {
LegacyReviewSDK.shared.submit(orderID: orderID) { approved in
completion(approved)
}
}
requestReview(for: "order-42") { approved in
print("Approved: \(approved)")
}
#The new way
enum LegacyReviewSDK {
static let shared = LegacyReviewSDK()
func submit(orderID: String, completion: @escaping (Bool, Error?) -> Void) {
completion(true, nil)
}
}
func requestReview(for orderID: String) async throws -> Bool {
try await withCheckedThrowingContinuation { continuation in
LegacyReviewSDK.shared.submit(orderID: orderID) { approved, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: approved)
}
}
}
}
func processOrder(_ orderID: String) async throws {
let approved = try await requestReview(for: orderID)
print("Approved: \(approved)")
}
#Why it matters
- Wraps any callback API in a single
async throwscall without touching or forking the original SDK withCheckedThrowingContinuationadds a runtime check in debug builds that catches double-resume and never-resume bugswithUnsafeContinuation/withUnsafeThrowingContinuationdrop that check for a small performance win once the wrapper is verified correct- Keeps the bridging code localized to one function instead of leaking completion-handler style through the rest of the call chain
#Gotcha
The continuation's resume must be called exactly once on every code path, including error and early-return branches — calling it twice is a runtime crash under the checked variants, and never calling it at all leaves the awaiting task suspended forever with no timeout. Audit every branch of the wrapped callback, especially ones that can fire more than once (like a delegate method), before trusting the wrapper.