I am using the following code for caching, the response received form the server has the following headers. Is there any header that needs to be set from the request side, for the caching to work for 10 seconds of age.
Connection Received Resopnse Headers= [Date: Sat, 12 Sep 2015 22:51:16 GMT, Transfer-Encoding: Identity, Server: Apache-Coyote/1.1, Content-Type: application/json;charset=UTF-8, Expires: Sat, 12 Sep 2015 22:51:26 GMT, Cache-Control: max-age=10, must-revalidate]
The mighty code which is not caching.
import UIKit
class HTTPJSONDonwload: NSObject , NSURLConnectionDataDelegate , NSURLConnectionDelegate {
static let httpjsonDownloader:HTTPJSONDonwload = HTTPJSONDonwload()
func startDownload(){
let serverRequest = getServerURL()
NSURLConnection(request: serverRequest, delegate: self, startImmediately: true)
}
func getServerURL() -> NSMutableURLRequest{
let request:NSMutableURLRequest = NSMutableURLRequest(URL:NSURL(string:"http://citiesfav-jcitiesj.rhcloud.com/Cache/getAllCities")! )
request.cachePolicy = NSURLRequestCachePolicy.UseProtocolCachePolicy
request.HTTPMethod = "POST"
return request
}
func connection(connection: NSURLConnection, didReceiveData data: NSData) {
print("Connection Data= \(NSString(data: data, encoding: NSUTF8StringEncoding))")
}
func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
print("Connection Received Resopnse Headers= \((response as! NSHTTPURLResponse).allHeaderFields)")
}
func connection(connection: NSURLConnection, willCacheResponse cachedResponse: NSCachedURLResponse) -> NSCachedURLResponse? {
print("Connection will cache Response")
return cachedResponse
}
}
After removing must-revalidate from the header it was still fetching the request.
Connection Received Resopnse Headers= [Cache-Control: max-age=10, Transfer-Encoding: Identity, Date: Sun, 13 Sep 2015 18:35:43 GMT, Content-Type: application/json;charset=UTF-8, Server: Apache-Coyote/1.1, Expires: Sun, 13 Sep 2015 18:35:53 GMT]
Later findings show the POST request does get cached, but does not work like GET, where max-age is considered.
func startDownload(){
let serverRequest = getServerURL()
let cache = NSURLCache.sharedURLCache()
let response = cache.cachedResponseForRequest(serverRequest)
if response != nil {
serverRequest.cachePolicy = NSURLRequestCachePolicy.ReturnCacheDataDontLoad
}
NSURLConnection(request: serverRequest, delegate: self, startImmediately: true)
}
You need to use GET
instead of POST
.
The issue is that you're request is a POST
.
func getServerURL() -> NSMutableURLRequest{
...
request.HTTPMethod = "POST"
...
}
In general, POST
requests are used to create (or sometimes also to update) a resource on the server. Reusing the cached response for a creation or update request doesn't make much sense because you have to send the request to the server anyway (otherwise nothing is going to be created or updated). It seems that iOS automatically circumvents the cache on POST
requests.
In your particular case, however, you don't really need the POST
because you're merely reading data from the server. That means you should use a GET
request instead.
func getServerURL() -> NSMutableURLRequest{
...
request.HTTPMethod = "GET"
...
}
I verified that the iOS system actually reuses the cache with the following snippet.
let d = HTTPJSONDonwload()
// Initial request. Can not reuse cache.
d.startDownload()
// Subsequent request after 5 seconds. Should be able to reuse the cache.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5 * NSEC_PER_SEC)), dispatch_get_main_queue()) {
d.startDownload()
}
// Subsequent request after 11 seconds. Cannot reuse the cache because
// the expiration timeout is 10 seconds.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(11 * NSEC_PER_SEC)), dispatch_get_main_queue()) {
d.startDownload()
}
When I run this in the simulator and monitor the network calls with Charles Proxy, I indeed only see two events:
The first call is the initial request
and the second call is the third request which was issued after a delay of 11 seconds.
Note that the second request, which was issued after 5 seconds, does not appear which means that the response was retrieved from the cache. The delegate methods of NSURLConnection
will, however, still be called just as if the response came from the network. With the logging output in your code you'll, therefore, see all three requests on the console.
Connection Received Resopnse Headers= [Server: Apache-Coyote/1.1, Content-Type: application/json;charset=UTF-8, Keep-Alive: timeout=15, max=100, Proxy-Connection: Keep-alive, Date: Mon, 14 Sep 2015 06:28:05 GMT, Content-Encoding: gzip, Content-Length: 36, Cache-Control: max-age=10, Vary: Accept-Encoding]
Connection Data= Optional({"1":"New York"})
Connection will cache Response
Connection Received Resopnse Headers= [Server: Apache-Coyote/1.1, Content-Type: application/json;charset=UTF-8, Keep-Alive: timeout=15, max=100, Proxy-Connection: Keep-alive, Date: Mon, 14 Sep 2015 06:28:05 GMT, Content-Encoding: gzip, Content-Length: 36, Cache-Control: max-age=10, Vary: Accept-Encoding]
Connection Data= Optional({"1":"New York"})
Connection Received Resopnse Headers= [Server: Apache-Coyote/1.1, Content-Type: application/json;charset=UTF-8, Keep-Alive: timeout=15, max=99, Proxy-Connection: Keep-alive, Date: Mon, 14 Sep 2015 06:28:16 GMT, Content-Encoding: gzip, Content-Length: 36, Cache-Control: max-age=10, Vary: Accept-Encoding]
Connection Data= Optional({"1":"New York"})
Connection will cache Response
Note that there is no Connection will cache Response
after the second request because the response was retrieved from the cache and there is no point in caching it again.