title: Cancelling an async/await Network Request
body: <p>I have a networking layer that currently uses completion handlers to deliver a result on the operation is complete.</p>
  <p>As I support a number of iOS versions, I instead extend the network layer within the app to provide support for Combine. I'd like to extend this to now also a support Async/Await but I am struggling to understand how I can achieve this in a way that allows me to cancel requests.</p>
  <p>The basic implementation looks like;</p>
  <pre class="lang-swift prettyprint-override"><code>
  protocol HTTPClientTask {
      func cancel()
  }
  
  protocol HTTPClient {
      typealias Result = Swift.Result&lt;(data: Data, response: HTTPURLResponse), Error&gt;
      @discardableResult
      func dispatch(_ request: URLRequest, completion: @escaping (Result) -&gt; Void) -&gt; HTTPClientTask
  }
  
  final class URLSessionHTTPClient: HTTPClient {
      
      private let session: URLSession
      
      init(session: URLSession) {
          self.session = session
      }
      
      func dispatch(_ request: URLRequest, completion: @escaping (HTTPClient.Result) -&gt; Void) -&gt; HTTPClientTask {
          let task = session.dataTask(with: request) { data, response, error in
              completion(Result {
                  if let error = error {
                      throw error
                  } else if let data = data, let response = response as? HTTPURLResponse {
                      return (data, response)
                  } else {
                      throw UnexpectedValuesRepresentation()
                  }
              })
          }
          task.resume()
          return URLSessionTaskWrapper(wrapped: task)
      }
  }
  
  private extension URLSessionHTTPClient {
      struct UnexpectedValuesRepresentation: Error {}
      
      struct URLSessionTaskWrapper: HTTPClientTask {
          let wrapped: URLSessionTask
          
          func cancel() {
              wrapped.cancel()
          }
      }
  }
  </code></pre>
  <p>It very simply provides an abstraction that allows me to inject a <code>URLSession</code> instance.</p>
  <p>By returning <code>HTTPClientTask</code> I can call <code>cancel</code> from a client and end the request.</p>
  <p>I extend this in a client app using <code>Combine</code> as follows;</p>
  <pre class="lang-swift prettyprint-override"><code>extension HTTPClient {
      typealias Publisher = AnyPublisher&lt;(data: Data, response: HTTPURLResponse), Error&gt;
  
      func dispatchPublisher(for request: URLRequest) -&gt; Publisher {
          var task: HTTPClientTask?
  
          return Deferred {
              Future { completion in
                  task = self.dispatch(request, completion: completion)
              }
          }
          .handleEvents(receiveCancel: { task?.cancel() })
          .eraseToAnyPublisher()
      }
  }
  </code></pre>
  <p>As you can see I now have an interface that supports canceling tasks.</p>
  <p>Using <code>async/await</code> however, I am unsure what this should look like, how I can provide a mechanism for canceling requests.</p>
  <p>My current attempt is;</p>
  <pre class="lang-swift prettyprint-override"><code>extension HTTPClient {
      func dispatch(_ request: URLRequest) async -&gt; HTTPClient.Result {
  
          let task = Task { () -&gt; (data: Data, response: HTTPURLResponse) in
              return try await withCheckedThrowingContinuation { continuation in
                  self.dispatch(request) { result in
                      switch result {
                      case let .success(values): continuation.resume(returning: values)
                      case let .failure(error): continuation.resume(throwing: error)
                      }
                  }
              }
          }
  
          do {
              let output = try await task.value
              return .success(output)
          } catch {
              return .failure(error)
          }
      }
  }
  </code></pre>
  <p>However this simply provides the <code>async</code> implementation, I am unable to cancel this.</p>
  <p>How should this be handled?</p>