I'm not sure what the deal is here, but the function:
class func requestIsCacheEquivalent(a: NSURLRequest, toRequest b: NSURLRequest) -> Bool
is never called within my NSURLProtocol subclass. I've even seen cases of the cache being used (verified by using a network proxy and seeing no calls being made) but this method just never gets invoked. I'm at a loss for why this is.
The problem I'm trying to solve is that I have requests that I'd like to cache data for, but these requests have a signature parameter that's different for each one (kind of like a nonce). This makes it so the cache keys are not the same despite the data being equivalent.
To go into explicit detail:
- I fire a request with a custom signature (like this:
www.example.com?param1=1¶m2=2&signature=1abcdefabc312093
) - The request comes back with an Etag
- The Etag is supposed to be managed by the NSURLCache but since it thinks that a different request (
www.example.com?param1=1¶m2=2&signature=1abdabcda3359809823
) is being made it doesn't bother.
I thought that using NSURLProtocol would solve all my problems since Apple's docs say:
class func requestIsCacheEquivalent(_ aRequest: NSURLRequest,
toRequest bRequest: NSURLRequest) -> Bool
YES if aRequest and bRequest are equivalent for cache purposes, NO otherwise. Requests are considered equivalent for cache purposes if and only if they would be handled by the same protocol and that protocol declares them equivalent after performing implementation-specific checks.
Sadly, the function is never called. I don't know what the problem could be...
class WWURLProtocol : NSURLProtocol, NSURLSessionDataDelegate {
var dataTask: NSURLSessionDataTask?
var session: NSURLSession!
var trueRequest: NSURLRequest!
private lazy var netOpsQueue: NSOperationQueue! = NSOperationQueue()
private lazy var delegateOpsQueue: NSOperationQueue! = NSOperationQueue()
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
println("can init with request called")
return true
}
override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest {
println("canonical request for request called")
return request
}
override class func requestIsCacheEquivalent(a: NSURLRequest, toRequest b: NSURLRequest) -> Bool {
// never ever called?!?
let cacheKeyA = a.allHTTPHeaderFields?["CacheKey"] as? String
let cacheKeyB = b.allHTTPHeaderFields?["CacheKey"] as? String
println("request is cache equivalent? \(cacheKeyA) == \(cacheKeyB)")
return cacheKeyA == cacheKeyB
}
override func startLoading() {
println("start loading")
let sharedSession = NSURLSession.sharedSession()
let config = sharedSession.configuration
config.URLCache = NSURLCache.sharedURLCache()
self.session = NSURLSession(configuration: config, delegate: self, delegateQueue: self.delegateOpsQueue)
dataTask = session.dataTaskWithRequest(request, nil)
dataTask?.resume()
}
override func stopLoading() {
println("stop loading")
dataTask?.cancel()
}
//SessionDelegate
func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
println("did become invalid with error")
client?.URLProtocol(self, didFailWithError: error!)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
println("did complete with error")
if error == nil {
client?.URLProtocolDidFinishLoading(self)
} else {
client?.URLProtocol(self, didFailWithError: error!)
}
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
println("did receive response")
client?.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .Allowed)
completionHandler(.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
println("did receive data called")
client?.URLProtocol(self, didLoadData: data)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: (NSCachedURLResponse!) -> Void) {
println("will cache response called")
client?.URLProtocol(self, cachedResponseIsValid: proposedResponse)
completionHandler(proposedResponse)
}
I registered the protocol in my app delegate as follows:
NSURLProtocol.registerClass(WWURLProtocol.self)
I trigger the protocol as follows:
@IBAction func requestData(endpointString: String) {
let url = NSURL(string: endpointString)
let request = NSMutableURLRequest(URL: url!)
var cacheKey = endpointString
request.setValue("\(endpointString)", forHTTPHeaderField: "CacheKey")
request.cachePolicy = .UseProtocolCachePolicy
NSURLConnection.sendAsynchronousRequest(request, queue: netOpsQueue) { (response, data, error) -> Void in
if data != nil {
println("succeeded with data:\(NSString(data: data, encoding: NSUTF8StringEncoding)))")
}
}
}