0

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
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
7vikram7
  • 2,764
  • 1
  • 25
  • 43
  • Please take more time to format your questions. I've tried to do some cleanup but you shouldn't leave it to someone else to do it for you. – matt Apr 13 '23 at 18:49
  • Unrelated but **never** declare properties IUO which are clearly initialized with non-optional values (the `endpoint` parameter is missing by the way). Remove the exclamation marks and move the `super` call to the end of the method. Surprisingly the code still compiles. And both properties should be constants (`let`). – vadian Apr 14 '23 at 08:19
  • You need to be `URLSessionDataDelegate` compliant, which is a "sub protocol" of `URLSessionDelegate`. Since you aren't it won't call the method I guess. Also, now there is a `@Sendable` in the method in recent Swift, I wonder if it also could play a difference, but I don't think so. – Larme Apr 14 '23 at 09:22
  • Does my answer resolves your issue then? Does it trigger the call f the delegate method? – Larme Apr 26 '23 at 08:19

1 Answers1

0

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.

Larme
  • 24,190
  • 6
  • 51
  • 81