Bridging legacy callbacks/Combine to async/await
Wrap completion-handlers using withCheckedContinuation / withCheckedThrowingContinuation and bridge Combine publishers into AsyncSequence for simple for await loops.
Why bridge?
Goal: Use continuations to wrap callbacks and treat Combine streams as AsyncSequence.
- Continuations (non-throwing / throwing)
- URLSession bridging example
- Combine →
for awaitconsumption
Non-throwing continuation
Use withCheckedContinuation when the legacy API cannot fail. Resume exactly once.
// Old-style API
func fetchName(completion: @escaping (String) -> Void) {
// simulate async
Task { try? await Task.sleep(nanoseconds: 80_000_000); completion("Ana") }
}
// Async wrapper using continuation (non-throwing)
func fetchName() async -> String {
await withCheckedContinuation { (cont: CheckedContinuation<String, Never>) in
fetchName { value in cont.resume(returning: value) }
}
}
Task {
let name = await fetchName()
print("name:", name) // "Ana"
}All lessons in this course
- Producer/consumer, pipelines, timeouts
- Bridging legacy callbacks/Combine to async/await
- Testing async code (XCTest)