Last active
August 19, 2021 15:33
-
-
Save inamiy/0f11034f3dd658ad699a9ac5d2714b2d to your computer and use it in GitHub Desktop.
Swift async/await: Re-inventing URLSession's `func data(for:) async throws` https://twitter.com/inamiy/status/1428378193170288645
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| let url = URL(string: "https://httpbin.org/get")! | |
| @main | |
| struct Main { | |
| static func main() async throws { | |
| print("1") | |
| let task1 = Task.init { () -> Data? in | |
| do { | |
| return try await download(url: url) | |
| // return try await uncancellableDownload(url: url) | |
| } catch { | |
| print("download error: ", error) | |
| return nil | |
| } | |
| } | |
| // NOTE: Requires small delay to start running `URLSession.shared.dataTask` | |
| await Task.sleep(1) | |
| print("2. will cancel") | |
| task1.cancel() | |
| await Task.sleep(2_000_000_000) | |
| print("done") | |
| let data = try await task1.value | |
| print(String(data: data ?? Data(), encoding: .utf8)!) | |
| } | |
| } | |
| // https://twitter.com/inamiy/status/1428369626631524367 | |
| class Box<T> { | |
| var value: T | |
| init(_ value: T) { self.value = value } | |
| } | |
| // https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md#cancellation-handlers | |
| func download(url: URL) async throws -> Data? { | |
| let urlSessionTask: Box<URLSessionTask?> = .init(nil) | |
| return try await withTaskCancellationHandler { | |
| let value: Data? = try await withUnsafeThrowingContinuation { continuation in | |
| urlSessionTask.value = URLSession.shared.dataTask(with: url) { data, _, error in | |
| if let error = error { | |
| continuation.resume(throwing: error) | |
| } else { | |
| continuation.resume(returning: data) | |
| } | |
| } | |
| urlSessionTask.value?.resume() | |
| } | |
| return value | |
| } onCancel: { | |
| print("cancelling", urlSessionTask.value) | |
| urlSessionTask.value?.cancel() | |
| } | |
| } | |
| func uncancellableDownload(url: URL) async throws -> Data? { | |
| var urlSessionTask: URLSessionTask? | |
| return try await withTaskCancellationHandler { | |
| let value: Data? = try await withUnsafeThrowingContinuation { continuation in | |
| urlSessionTask = URLSession.shared.dataTask(with: url) { data, _, error in | |
| if let error = error { | |
| continuation.resume(throwing: error) | |
| } else { | |
| continuation.resume(returning: data) | |
| } | |
| } | |
| urlSessionTask?.resume() | |
| } | |
| return value | |
| } onCancel: { [urlSessionTask] in // WARNING: strong capture is BAD which is always nil | |
| print("cancelling", urlSessionTask) | |
| urlSessionTask?.cancel() | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment