How to call URLSession Cache handler delegate method in async await?

Hello!
I try to find the way to customize the caching response handler in URLSession async await methods.

When I call the async await URLSession data method func data(for request: URLRequest, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) with delegate parameter, the cache handler delegate method is not called.
On the other hand, when I call URLSession data task method func dataTask(with request: URLRequest) -> URLSessionDataTask and set the delegate instance in it with same condition, the cache handler delegate method is called as I expected.

Does anyone understand what happens here and how to customize cache handling in async await?
Supplement: In async await case, after first request I put CachePolicy.returnCacheDataDontLoad in the URLRequest instance and try another.Then I get the cache response. So even though the cache handler is not called, default caching seems to work well.

Here's the code.

Base code

import UIKit

// create URL Request and URL Session
let apiUrl:URL = URL(string: "https://api.github.com/search/repositories?q=swift")!
var urlRequest:URLRequest = .init(url: apiUrl)
let session = URLSession.init(configuration: .default)

// Simple Delegate Implementation to check what delegate method is called.
class Delegate: NSObject, URLSessionDataDelegate {
    
    func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) {
        print("didCreateTask")
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        print("didCollectMetrics")
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let error {
            print("didCompletionWithError")
            return
        }
        print("didcompletion")
    }

    //DataTask
    
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        print("didReceiveData")
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) async -> CachedURLResponse? {
        print("willlcache")
        return proposedResponse
    }
}

The dataTask case(The cache handler is called).

let task = session.dataTask(with: urlRequest)
task.delegate = Delegate()
task.resume()

The async await data method case(The cache handler is not called).

Task {
    do {
        let (data, response) = try await session.data(for: urlRequest, delegate: Delegate())
        print(data)
        print(response)
    } catch {
        print(error)
    }
}

Thank you for your help!

With async-await, the delegate parameter passed to data(for:delegate:) is URLSessionTaskDelegate. The methods for caching are part of URLSessionDataDelegate (which inherits from URLSessionTaskDelegate).

1 Like

Thank you for your reply.

Yes,it is! But when I call URLSession.dataTask(with:)and set the delegate property of the task, the URLSessionTaskDelegate's caching handling method is called. And the URLSessionTask.delegate property is also URLSessionTaskDelegate? type, not URLSessionDataDelegate. It still calls URLSessionDataDelegate methods.

Anyway, my point is how to call the cache handler in async await. It seems strange that async await URLSession method can't call the caching handling method of URLSessionDataDelegate.

I assume Foundation is checking the type of the delegate and invoking the callbacks as appropriate. I'm not sure why it doesn't do the same with async-await. Probably worth filing a bug with Apple.

The workaround would be to use the non-async methods under the hood and wrap them. :face_with_diagonal_mouth:

2 Likes

I see.
I'll file it as a bug to Apple.

Thank you for your advise.

I'll file it as a bug to Apple.

Much appreciated.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like