iOS URLSession delegate method (session, dataTask, willCacheResponse, proposedResponse) not called

427 Views Asked by At

I need the API responses to be cached for a longer duration. Hence, I am trying to modify the 'max-age' header for the API response.

But the

urlSession(_ session: URLSession, 
          dataTask: URLSessionDataTask, 
          willCacheResponse proposedResponse: CachedURLResponse, 
          completionHandler: @escaping (CachedURLResponse?
) -> Void) 

method is not called to be able to do it.

API Client Class code :

public class APIClient: NSObject, URLSessionDelegate {
    var endPoint: String!
    var urlSession: URLSession!
    
    public init(urlSession: URLSession = .shared) {
        super.init()
        self.endPoint = endPoint
        
        let sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default
            sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
            sessionConfiguration.urlCache = .shared
    
            self.urlSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
    
    }
    
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {
    
        if dataTask.currentRequest?.cachePolicy == .useProtocolCachePolicy {
            let newResponse = proposedResponse.response(withExpirationDuration: 60)
            completionHandler(newResponse)
        }else {
            completionHandler(proposedResponse)
        }
    }
    
    private func dispatch<ReturnType: Decodable>(request: URLRequest) -> AnyPublisher<ReturnType, NetworkError> {
        let url = request.url // For troubleshooting only
        return urlSession
            .dataTaskPublisher(for: request)
        
        // Map on Request response
            .tryMap({ data, response in
                return data
            })
            .decode(type: ReturnType.self, decoder: JSONDecoder())
            .mapError { error in
            }
            .eraseToAnyPublisher()
    }
}

API call:

func makeAPICall() -> AnyPublisher<ContentConfigResponseModel, NetworkError> {
    var request = // create request data
    let apiClient = APIClient(endPoint: request.path)
    return apiClient.dispatch(request)
}

I have with tried multiple cachePolicy. According to some suggestions I have tried implementing

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) async -> URLSession.ResponseDisposition {
    return URLSession.ResponseDisposition.allow
}
1

There are 1 best solutions below

0
Larme On

Delegate methods not being called are usually for 3 reasons:

  • delegate is nil: has been dealloc or not set (not set at all, or not set on the correct instance)
  • delegate method isn't correctly written: signature are different, it's not the same method
  • delegate object isn't set as compliant with the protocol

Your issue is the last one.

urlSession(_:dataTask:willCacheResponse:completionHandler:) is a URLSessionDataDelegate method.

It's a "subprotocol" of URLSessionDelegate.

APIClient conforms only to URLSessionDelegate not URLSessionDataDelegate even if it implements a method that has the same signature.

So public class APIClient: NSObject, URLSessionDelegate should be public class APIClient: NSObject, URLSessionDelegate, URLSessionDataDelegate

We can check also the URLSession source code that points it out:

if let delegate = task.session.delegate as? URLSessionDataDelegate {
    delegate.urlSession(task.session as! URLSession, dataTask: task, willCacheResponse: cacheable) { (actualCacheable) in
        if let actualCacheable = actualCacheable {
            cache.storeCachedResponse(actualCacheable, for: task)
        }
    }
} else {
    cache.storeCachedResponse(cacheable, for: task)
}

You don't pass the test if let delegate = task.session.delegate as? URLSessionDataDelegate.