From ad3e55e40c2d5b69439de2025a71f76c34fc1a1a Mon Sep 17 00:00:00 2001 From: Edwin Vermeer Date: Tue, 23 Aug 2016 12:46:51 +0200 Subject: [PATCH] use Cache-Control en Pragma --- EVURLCache.podspec | 2 +- EVURLCache/Pod/EVURLCache.swift | 36 +++++++++++++++++++++++++-------- README.md | 16 +++++++++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/EVURLCache.podspec b/EVURLCache.podspec index 5d67383..51cb994 100644 --- a/EVURLCache.podspec +++ b/EVURLCache.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| # s.name = "EVURLCache" -s.version = "2.9.0" +s.version = "2.10.0" s.summary = "NSURLCache subclass for handeling all web requests that use NSURLRequest" s.description = "This is a NSURLCache subclass for handeling all web requests that use NSURLRequest. (This includes UIWebView)" s.homepage = "https://github.com/evermeer/EVURLCache" diff --git a/EVURLCache/Pod/EVURLCache.swift b/EVURLCache/Pod/EVURLCache.swift index 4050f27..b047602 100644 --- a/EVURLCache/Pod/EVURLCache.swift +++ b/EVURLCache/Pod/EVURLCache.swift @@ -18,8 +18,7 @@ import Foundation public class EVURLCache: NSURLCache { public static var URLCACHE_CACHE_KEY = "MobileAppCacheKey" // Add this header variable to the response if you want to save the response using this key as the filename. - public static var URLCACHE_EXPIRATION_AGE_KEY = "MobileAppExpirationAgeKey" // Add this header variable to the response to set the expiration age. - public static var MAX_AGE = "604800000" // The default maximum age of a cached file in miliseconds. (1 week) + public static var MAX_AGE = "604800" // The default maximum age of a cached file in seconds. (1 week) public static var PRE_CACHE_FOLDER = "PreCache" // The folder in your app with the prefilled cache content public static var CACHE_FOLDER = "Cache" // The folder in the Documents folder where cached files will be saved public static var MAX_FILE_SIZE = 24 // The maximum file size that will be cached (2^24 = 16MB) @@ -93,8 +92,8 @@ public class EVURLCache: NSURLCache { // Check file status only if we have network, otherwise return it anyway. if EVURLCache.networkAvailable() { if cacheItemExpired(request, storagePath: storagePath) { - let maxAge: String = request.valueForHTTPHeaderField(EVURLCache.URLCACHE_EXPIRATION_AGE_KEY) ?? EVURLCache.MAX_AGE - EVURLCache.debugLog("CACHE item older than \(maxAge) maxAgeHours") + let maxAge: String = request.valueForHTTPHeaderField("Access-Control-Max-Age") ?? EVURLCache.MAX_AGE + EVURLCache.debugLog("CACHE item older than \(maxAge) seconds") return nil } } @@ -129,15 +128,36 @@ public class EVURLCache: NSURLCache { } } - // check if caching is allowed + var shouldSkipCache: String? = nil + + // check if caching is allowed according to the request if request.cachePolicy == NSURLRequestCachePolicy.ReloadIgnoringCacheData { + shouldSkipCache = "request cache policy" + } + + // check if caching is allowed according to the response Cache-Control or Pragma header + if let httpResponse = cachedResponse.response as? NSHTTPURLResponse { + if let cacheControl = httpResponse.allHeaderFields["Cache-Control"] as? String { + if cacheControl.lowercaseString.containsString("no-cache") || cacheControl.lowercaseString.containsString("no-store") { + shouldSkipCache = "response cache control" + } + } + + if let cacheControl = httpResponse.allHeaderFields["Pragma"] as? String { + if cacheControl.lowercaseString.containsString("no-cache") { + shouldSkipCache = "response pragma" + } + } + } + + if shouldSkipCache != nil { // If the file is in the PreCache folder, then we do want to save a copy in case we are without internet connection let storagePath = EVURLCache.storagePathForRequest(request, rootPath: EVURLCache._preCacheDirectory) ?? "" if !NSFileManager.defaultManager().fileExistsAtPath(storagePath) { - EVURLCache.debugLog("CACHE not storing file, it's not allowed by the cachePolicy : \(request.URL)") + EVURLCache.debugLog("CACHE not storing file, it's not allowed by the \(shouldSkipCache) : \(request.URL)") return } - EVURLCache.debugLog("CACHE file in PreCache folder, overriding cachePolicy : \(request.URL)") + EVURLCache.debugLog("CACHE file in PreCache folder, overriding \(shouldSkipCache) : \(request.URL)") } // create storrage folder @@ -176,7 +196,7 @@ public class EVURLCache: NSURLCache { private func cacheItemExpired(request: NSURLRequest, storagePath: String) -> Bool { // Max cache age for request - let maxAge: String = request.valueForHTTPHeaderField(EVURLCache.URLCACHE_EXPIRATION_AGE_KEY) ?? EVURLCache.MAX_AGE + let maxAge: String = request.valueForHTTPHeaderField("Access-Control-Max-Age") ?? EVURLCache.MAX_AGE do { let attributes = try NSFileManager.defaultManager().attributesOfItemAtPath(storagePath) diff --git a/README.md b/README.md index b52f50d..ddeee79 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,22 @@ Since (most likely, see limitations) all files will be cached, you do not have t You can do a NSURLRequest and then in the connectionDidFinishLoading you can use the file from the cache. You can get the full path of that file by calling: EVURLCache.storagePathForRequest(theRequest) +## Controlling the cache + +EVURLCache respects the HTTP header variables 'Cache-Control' and 'Pragma' when these contain 'no-cache' or 'no-store' then the response will not be written to the cache. You do have to be aware that if the file is already in the cache because you have put it in the PreCache folder yourself or the file was previously fetched with different header variables, the file will be written to the cache in order to update it's contents and the HTTP header varialbes will be ignored. + +EVURLCache will also take into account the HTTP header variable 'Access-Control-Max-Age' when reading from the cache. When the content is older it will try to fetch it again. + +Caching is done based on the complete URL including the querystring parameters. If for some reason you want multiple URL's to be stored and fetched as the same cache item, then you can add the HTTP header variable (server side) MobileAppCacheKey + +Most webservers interpit url's' case insesnsitive. Since iOS and OSX (not always) have a case sensitive file system it could be that a URL is requested that do not have a case sensitive match on the file system. By default EVURLCache stores all files while converting the path to lowercase. If you do want a case sensitive match, then you could set the EVURLCache.FORCE_LOWERCASE to false + +You can influence the maximum file size that will be cached by EVURLCache. This is a setting that is handled by the NSURLCache base class. By default it's set to 16MB. You can influence this by setting the EVURLCache.MAX_FILE_SIZE. It's set as number of bits. So setting it to 24 will mean a cache size of 2^24 = 16MB + +You can influence the maximum total size that will be cached by EVURLCache. This is a setting that is handled by the NSURLCache base class. By default it's set to 256MB. You can influence this by setting the EVURLCache.MAX_CACHE_SIZE It's set as number of bits. So setting it to 30 will mean a cache size of 2^30 = 256MB. Make sure it's at least 16 times larger than the Maximum file size or the maximum file size will not be used + +If you want to see what EVURLCache is doing, then set EVURLCache.LOGGING to true + ## See the demo in action Follow these steps to see the demo app in action. Logging is enabled, so watch the output window to see what's happening.