0

I was trying to set custom headers for 'Cache-Control' to achieve a cache at client side(server side has 'Cache-Control: no-cache'). Trying to achieve following two major things.

  1. Response of some of the end points should be cached in memory and should have an expiration(user defined)
  2. Once expiration time is over, then app should ignore the cache and should fetch data from server.

I followed this link and was able to achieve first target but somehow even after expiry app is still using cache and not triggering any API calls. Not sure if 'max-age' set in header is ignored by the app. Please guide me if I am missing something here.

Here are the code snippets.

Session Configuration:

let sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.ephemeral
    sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
    sessionConfiguration.urlCache = .shared

    self.currentURLSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)

Request:

if let urlPath = URL(string: <WEB_API_END_POINT>){
        var aRequest = URLRequest(url: urlPath, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60)
        aRequest.addValue("private", forHTTPHeaderField: "Cache-Control")

        let aTask = self.currentURLSession.dataTask(with: aRequest)

        aTask.resume()

}

Caching logic:

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {       
    if proposedResponse.response.url?.path.contains("/employees") == true {
        let updatedResponse = proposedResponse.response(withExpirationDuration: 60)
        completionHandler(updatedResponse)
    } else {
        completionHandler(proposedResponse)
    }
}

CachedURLResponse Extension:

extension CachedURLResponse {
func response(withExpirationDuration duration: Int) -> CachedURLResponse {
    var cachedResponse = self
    if let httpResponse = cachedResponse.response as? HTTPURLResponse, var headers = httpResponse.allHeaderFields as? [String : String], let url = httpResponse.url{

        headers["Cache-Control"] = "max-age=\(duration)"
        headers.removeValue(forKey: "Expires")
        headers.removeValue(forKey: "s-maxage")

        if let newResponse = HTTPURLResponse(url: url, statusCode: httpResponse.statusCode, httpVersion: "HTTP/1.1", headerFields: headers) {
            cachedResponse = CachedURLResponse(response: newResponse, data: cachedResponse.data, userInfo: headers, storagePolicy: .allowedInMemoryOnly)
        }
    }
    return cachedResponse
}

}

Om Prakash
  • 11
  • 2

1 Answers1

0

Was able to fix it at my own. Still sharing the answer in case if helps someone else in need.

Added 'Cache-Control' response header in server response and there we have 'max-age: 60', which indicates that response can be valid till 60 seconds only. So till 60 seconds, app will cache that data and after 60 seconds if making another request, this will fetch fresh data from server.

With that, At client side other than defining your cache policy, nothing else is required. You can do this either for entire URL Session:

let sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.ephemeral
sessionConfiguration.requestCachePolicy = .useProtocolCachePolicy
sessionConfiguration.urlCache = .shared

self.currentURLSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)

Or can do it for specific request.

if let urlPath = URL(string: <WEB_API_END_POINT>) {
    var aRequest = URLRequest(url: urlPath, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60)
    
    let aTask = self.currentURLSession.dataTask(with: aRequest)
    aTask.resume()
}
Om Prakash
  • 11
  • 2