Skip to content

Commit

Permalink
Merge pull request #18 from GrioSF/master
Browse files Browse the repository at this point in the history
Fix for bug where URLCACHE_EXPIRATION_AGE_KEY doesn't really work
  • Loading branch information
evermeer committed Apr 23, 2016
2 parents 28a39d5 + 2b63480 commit ab9eeb8
Showing 1 changed file with 53 additions and 42 deletions.
95 changes: 53 additions & 42 deletions EVURLCache/Pod/EVURLCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import ReachabilitySwift
#endif

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)
Expand All @@ -30,22 +30,22 @@ public class EVURLCache : NSURLCache {
public static var _preCacheDirectory: String!
public static var RECREATE_CACHE_RESPONSE = true // There is a difrence between unarchiving and recreating. I have to find out what.
private static var _filter = { _ in return true } as ((request: NSURLRequest) -> Bool)

// Activate EVURLCache
public class func activate() {
// set caching paths
_cacheDirectory = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]).URLByAppendingPathComponent(CACHE_FOLDER).absoluteString
_preCacheDirectory = NSURL(fileURLWithPath: NSBundle.mainBundle().resourcePath!).URLByAppendingPathComponent(PRE_CACHE_FOLDER).absoluteString

let urlCache = EVURLCache(memoryCapacity: 1<<MAX_FILE_SIZE, diskCapacity: 1<<MAX_CACHE_SIZE, diskPath: _cacheDirectory)

NSURLCache.setSharedURLCache(urlCache)
}

public class func filter (filterFor: ((request: NSURLRequest) -> Bool)) {
_filter = filterFor
}

// Log a message with info if enabled
public static func debugLog<T>(object: T, filename: String = __FILE__, line: Int = __LINE__, funcname: String = __FUNCTION__) {
if LOGGING {
Expand All @@ -56,7 +56,7 @@ public class EVURLCache : NSURLCache {
NSLog("\(dateFormatter.stringFromDate(NSDate())) \(process.processName))[\(process.processIdentifier):\(threadId)] \((filename as NSString).lastPathComponent)(\(line)) \(funcname):\r\t\(object)\n")
}
}

// Will be called by a NSURLConnection when it's wants to know if there is something in the cache.
public override func cachedResponseForRequest(request: NSURLRequest) -> NSCachedURLResponse? {
guard let url = request.URL else {
Expand All @@ -73,13 +73,13 @@ public class EVURLCache : NSURLCache {
EVURLCache.debugLog("CACHE skipped because of filter");
return nil
}

// is caching allowed
if ((request.cachePolicy == NSURLRequestCachePolicy.ReloadIgnoringCacheData || url.absoluteString.hasPrefix("file:/") || url.absoluteString.hasPrefix("data:")) && EVURLCache.networkAvailable()) {
EVURLCache.debugLog("CACHE not allowed for \(url)");
return nil;
}

// Is the file in the cache? If not, is the file in the PreCache?
var storagePath: String = EVURLCache.storagePathForRequest(request, rootPath: EVURLCache._cacheDirectory)
if !NSFileManager.defaultManager().fileExistsAtPath(storagePath) {
Expand All @@ -89,27 +89,16 @@ public class EVURLCache : NSURLCache {
return nil;
}
}

// Check file status only if we have network, otherwise return it anyway.
if EVURLCache.networkAvailable() {
// Max cache age for request
let maxAge:String = request.valueForHTTPHeaderField(EVURLCache.URLCACHE_EXPIRATION_AGE_KEY) ?? EVURLCache.MAX_AGE

do {
let attributes = try NSFileManager.defaultManager().attributesOfItemAtPath(storagePath)
if let modDate:NSDate = attributes[NSFileModificationDate] as? NSDate {
// Test if the file is older than the max age
if let threshold: NSTimeInterval = Double(maxAge) {
let modificationTimeSinceNow:NSTimeInterval? = -modDate.timeIntervalSinceNow
if modificationTimeSinceNow > threshold {
EVURLCache.debugLog("CACHE item older than \(maxAge) maxAgeHours");
return nil
}
}
}
} catch {}
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");
return nil
}
}

// Read object from file
if let response = NSKeyedUnarchiver.unarchiveObjectWithFile(storagePath) as? NSCachedURLResponse {
EVURLCache.debugLog("Returning cached data from \(storagePath)");
Expand All @@ -127,7 +116,7 @@ public class EVURLCache : NSURLCache {
}
return nil
}

// Will be called by NSURLConnection when a request is complete.
public override func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest request: NSURLRequest) {
if !EVURLCache._filter(request: request) {
Expand All @@ -139,7 +128,7 @@ public class EVURLCache : NSURLCache {
return
}
}

// check if caching is allowed
if request.cachePolicy == NSURLRequestCachePolicy.ReloadIgnoringCacheData {
// If the file is in the PreCache folder, then we do want to save a copy in case we are without internet connection
Expand All @@ -150,7 +139,7 @@ public class EVURLCache : NSURLCache {
}
EVURLCache.debugLog("CACHE file in PreCache folder, overriding cachePolicy : \(request.URL)");
}

// create storrage folder
let storagePath: String = EVURLCache.storagePathForRequest(request, rootPath: EVURLCache._cacheDirectory)
if var storageDirectory: String = NSURL(fileURLWithPath: "\(storagePath)").URLByDeletingLastPathComponent?.absoluteString.stringByRemovingPercentEncoding {
Expand All @@ -161,10 +150,17 @@ public class EVURLCache : NSURLCache {
try NSFileManager.defaultManager().createDirectoryAtPath(storageDirectory, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
EVURLCache.debugLog("Error creating cache directory \(storageDirectory)");
EVURLCache.debugLog("Error \(error.debugDescription)");
EVURLCache.debugLog("Error \(error.debugDescription)");
}
}

if let previousResponse = NSKeyedUnarchiver.unarchiveObjectWithFile(storagePath) as? NSCachedURLResponse {
if previousResponse.data == cachedResponse.data && !cacheItemExpired(request, storagePath: storagePath) {
EVURLCache.debugLog("CACHE not rewriting stored file");
return
}
}

// save file
EVURLCache.debugLog("Writing data to \(storagePath)");
if !NSKeyedArchiver.archiveRootObject(cachedResponse, toFile: storagePath) {
Expand All @@ -177,8 +173,26 @@ 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

do {
let attributes = try NSFileManager.defaultManager().attributesOfItemAtPath(storagePath)
if let modDate:NSDate = attributes[NSFileModificationDate] as? NSDate {
// Test if the file is older than the max age
if let threshold: NSTimeInterval = Double(maxAge) {
let modificationTimeSinceNow:NSTimeInterval? = -modDate.timeIntervalSinceNow
return modificationTimeSinceNow > threshold
}
}
} catch {}

return false
}


// return the path if the file for the request is in the PreCache or Cache.
public static func storagePathForRequest(request: NSURLRequest) -> String? {
var storagePath: String? = EVURLCache.storagePathForRequest(request, rootPath: EVURLCache._cacheDirectory)
Expand All @@ -192,10 +206,10 @@ public class EVURLCache : NSURLCache {
}

// build up the complete storrage path for a request plus root folder.
public static func storagePathForRequest(request: NSURLRequest, rootPath: String) -> String {
public static func storagePathForRequest(request: NSURLRequest, rootPath: String) -> String {
var localUrl: String!
let host: String = request.URL?.host ?? "default"

// The filename could be forced by the remote server. This could be used to force multiple url's to the same cache file
if let cacheKey = request.valueForHTTPHeaderField(URLCACHE_CACHE_KEY) {
localUrl = "\(host)/\(cacheKey)"
Expand All @@ -206,7 +220,7 @@ public class EVURLCache : NSURLCache {
NSLog("WARNING: Unable to get the path from the request: \(request)")
}
}

// Without an extension it's treated as a folder and the file will be called index.html
if let storageFile: String = localUrl.componentsSeparatedByString("/").last {
if !storageFile.containsString(".") {
Expand All @@ -220,7 +234,7 @@ public class EVURLCache : NSURLCache {
} else {
localUrl = "\(rootPath)/\(localUrl)"
}

// Cleanup
if localUrl.hasPrefix("file:") {
localUrl = localUrl.substringFromIndex(localUrl.startIndex.advancedBy(5))
Expand All @@ -229,7 +243,7 @@ public class EVURLCache : NSURLCache {
localUrl = localUrl.stringByReplacingOccurrencesOfString("//", withString: "/")
return localUrl
}

public static func addSkipBackupAttributeToItemAtURL(url: NSURL) -> Bool {
do {
try url.setResourceValue(NSNumber(bool: true), forKey: NSURLIsExcludedFromBackupKey)
Expand All @@ -239,7 +253,7 @@ public class EVURLCache : NSURLCache {
}
return false
}

// Check if we have a network connection
private static func networkAvailable() -> Bool {
let reachability: Reachability
Expand All @@ -252,6 +266,3 @@ public class EVURLCache : NSURLCache {
}
}
}



0 comments on commit ab9eeb8

Please sign in to comment.