NSURLProtocol. requestIsCacheEquivalent never called

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&param2=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&param2=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)))")
            }
        }
    
    }
    

    I think that in practice, the loading system just uses the canonicalized URL for cache purposes, and does a straight string comparison. I'm not certain, though. Try appending your nonce when you canonicalize it, in some form that is easily removable/detectable (in case it is already there).


    你的代码看起来没问题。你只需要遵循苹果有关URLProtocol文档。你可以尝试使用URLSession for NSURLConnection在新版本的iOS版本中被弃用。祝你好运。

    链接地址: http://www.djcxy.com/p/34788.html

    上一篇: NSURLProtocol + UIWebView +某些域=应用程序UI被冻结

    下一篇: NSURLProtocol。 requestIsCacheEquivalent从未调用过