# How to use withTaskCancellationHandler properly?

**URL:** <https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341>\
**Category:** Using Swift\
**Tags:** concurrency\
**Created:** [December 29, 2021, 8:29am UTC](https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341 "2021-12-29T08:29:45Z")\
**Posts on this page:** 1\
**Showing post:** 12

<div class="post-metadata">

**Author:** ![gabeshahbazian](https://avatars.discourse-cdn.com/v4/letter/g/34f0e0/32.png) [@gabeshahbazian](https://forums.swift.org/u/gabeshahbazian)\
**Post date:** [February 16, 2022, 9:21pm UTC](https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341/12 "2022-02-16T21:21:49Z")

</div>

Thanks for calling those points out @ole. Here's an updated (and slightly more generalized) version which should address them:

```swift
public extension URLSession {
    func data(for request: URLRequest) async throws -> (Data, URLResponse) {
        let dataTaskHolder = CancellableHolder<URLSessionDataTask>()

        return try await withTaskCancellationHandler(
            handler: {
                dataTaskHolder.cancel()
            },
            operation: {
                try await withCheckedThrowingContinuation { continuation in
                    dataTaskHolder.value = self.dataTask(with: request) { data, response, error in
                        guard let data = data, let response = response else {
                            let error = error ?? URLError(.badServerResponse)
                            return continuation.resume(throwing: error)
                        }

                        continuation.resume(returning: (data, response))
                    }

                    dataTaskHolder.value?.resume()
                }
            }
        )
    }
}

private class CancellableHolder<T: Cancellable>: @unchecked Sendable {
    private var lock = NSRecursiveLock()
    private var innerCancellable: T?

    private func synced<Result>(_ action: () throws -> Result) rethrows -> Result {
        lock.lock()
        defer { lock.unlock() }
        return try action()
    }

    var value: T? {
        get { synced { innerCancellable } }
        set { synced { innerCancellable = newValue } }
    }

    func cancel() {
        synced { innerCancellable?.cancel() }
    }
}

protocol Cancellable {
    func cancel()
}

extension URLSessionDataTask: Cancellable {}

```

What do you think?

---

_[View the full topic](https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341)._
