Skip to content

Instantly share code, notes, and snippets.

@inamiy
Last active August 19, 2021 15:33
Show Gist options
  • Select an option

  • Save inamiy/0f11034f3dd658ad699a9ac5d2714b2d to your computer and use it in GitHub Desktop.

Select an option

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
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