How to set NSURLRequest cache expiration? How to set NSURLRequest cache expiration? objective-c objective-c

How to set NSURLRequest cache expiration?


So, I found the solution.

The idea is to use connection:willCacheResponse: method. Before cache the response it will be executed and there we can change response and return new, or return nil and the response will not be cached. As I use AFNetworking, there is a nice method in operation:

- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;

Add code:

  [operation setCacheResponseBlock:^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {    if([connection currentRequest].cachePolicy == NSURLRequestUseProtocolCachePolicy) {      cachedResponse = [cachedResponse responseWithExpirationDuration:60];    }    return cachedResponse;  }];

Where responseWithExpirationDuration from category:

@interface NSCachedURLResponse (Expiration)-(NSCachedURLResponse*)responseWithExpirationDuration:(int)duration;@end@implementation NSCachedURLResponse (Expiration)-(NSCachedURLResponse*)responseWithExpirationDuration:(int)duration {  NSCachedURLResponse* cachedResponse = self;  NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)[cachedResponse response];  NSDictionary *headers = [httpResponse allHeaderFields];  NSMutableDictionary* newHeaders = [headers mutableCopy];  newHeaders[@"Cache-Control"] = [NSString stringWithFormat:@"max-age=%i", duration];  [newHeaders removeObjectForKey:@"Expires"];  [newHeaders removeObjectForKey:@"s-maxage"];  NSHTTPURLResponse* newResponse = [[NSHTTPURLResponse alloc] initWithURL:httpResponse.URL                                                               statusCode:httpResponse.statusCode                                                              HTTPVersion:@"HTTP/1.1"                                                             headerFields:newHeaders];  cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse                                                            data:[cachedResponse.data mutableCopy]                                                        userInfo:newHeaders                                                   storagePolicy:cachedResponse.storagePolicy];  return cachedResponse;}@end

So, we set expiration in seconds in http header according to http/1.1For that we need one of headers to be set up:Expires, Cache-Control: s-maxage or max-ageThen create new cache response, because the properties is read only, and return new object.


Swift equivalent of @HotJard's solution using URLSession

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: cachedResponse.storagePolicy)            }        }        return cachedResponse    }}

Then implement URLSessionDataDelegate protocol in your custom class

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)    }}

Don't forget to create your configuration and session, passing in the your custom class as the delegate reference e.g.

let session = URLSession(        configuration: URLSession.shared.configuration,        delegate: *delegateReference*,        delegateQueue: URLSession.shared.delegateQueue    )let task = session.dataTask(with: request)task.resume()


The expiration of responses in the NSURLCache is controlled via the Cache-Control header in the HTTP response.

EDIT I see you've updated your question. If the server doesn't provide the Cache-Control header in the response, it won't be cached. Every request to that endpoint will load the endpoint rather than return a cached response.