With Continuations we can bridge the non-async world and make it async. Continuations allow us to take the result of a completion block and turn it into an async flow. In this episode we will see the difference between checked and unchecked continuations as well as their throwing variants.
withCheckedContinuation Let's see how we can use this do adapt to a completion block based API. Note that I'm purposely ignoring the fact that image has an async version of this function ;) We can use a checked continuation to allow the runtime to enforce that is called at most one time. This is usually the case, but not always. Having the type present this information to callers is extremely useful. func downloadThumbnail(_ url: URL) async throws -> UIImage { let session = URLSession.shared await Task.sleep(NSEC_PER_SEC) let (data, response) = try await session.data(from: url, delegate: nil) guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { throw UnexpectedResponse() } guard let image = UIImage(data: data) else { throw InvalidImage() } let thumbSize = CGSize(width: 600, height: 600) let thumbRect = AVMakeRect(aspectRatio: image.size, insideRect: CGRect(origin: .zero, size: thumbSize)) return try await withCheckedThrowingContinuation { continuation in image.prepareThumbnail(of: thumbRect.size) { image in guard let image = image else { continuation.resume(throwing: InvalidImage()) return } continuation.resume(returning: image) } } }