NSURLПротокол. requestIsCacheEquivalent никогда не вызывался

Я не уверен, в чем здесь дело, но функция:

class func requestIsCacheEquivalent(a: NSURLRequest, toRequest b: NSURLRequest) -> Bool

никогда не вызывается в моем подклассе NSURLProtocol. Я даже видел случаи использования кеша (проверено с помощью сетевого прокси и отсутствием вызовов), но этот метод просто никогда не вызывается. Я в недоумении, почему это так.

Проблема, которую я пытаюсь решить, заключается в том, что у меня есть запросы, для которых я хотел бы кэшировать данные, но эти запросы имеют параметр подписи, который отличается для каждого из них (вроде одноразового номера). Это приводит к тому, что ключи кеша не совпадают, несмотря на то, что данные эквивалентны.

Чтобы перейти к явным деталям:

  1. Я запускаю запрос с пользовательской подписью (например: www.example.com?param1=1&param2=2&signature=1abcdefabc312093)
  2. Запрос возвращается с Etag
  3. Предполагается, что Etag управляется NSURLCache, но поскольку он считает, что выполняется другой запрос (www.example.com?param1=1&param2=2&signature=1abdabcda3359809823), он не беспокоится.

Я думал, что использование NSURLProtocol решит все мои проблемы, поскольку в документах Apple говорится:

class func requestIsCacheEquivalent(_ aRequest: NSURLRequest,
                      toRequest bRequest: NSURLRequest) -> Bool

YES, если aRequest и bRequest эквивалентны для кэширования, NO в противном случае. Запросы считаются эквивалентными для целей кэширования тогда и только тогда, когда они будут обрабатываться одним и тем же протоколом, и этот протокол объявляет их эквивалентными после выполнения проверок, специфичных для реализации.

К сожалению, функция никогда не вызывается. не знаю в чем может быть проблема...

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)
}

Я зарегистрировал протокол в моем делегате приложения следующим образом:

NSURLProtocol.registerClass(WWURLProtocol.self)

Я запускаю протокол следующим образом:

@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)))")
        }
    }

}

person fjlksahfob    schedule 19.01.2015    source источник


Ответы (2)


Я думаю, что на практике система загрузки просто использует канонизированный URL-адрес для кэширования и выполняет прямое сравнение строк. Хотя я не уверен. Попробуйте добавить свой одноразовый номер, когда вы канонизируете его, в какой-либо форме, которую легко удалить/обнаружить (если она уже есть).

person dgatwood    schedule 28.10.2015

С вашим кодом все в порядке. Вы просто следуете документам Apple о URLProtocol. Вы можете попробовать использовать URLSession, поскольку NSURLConnection устарело в более новой версии iOS. Удачи.

person Xingxing    schedule 03.03.2017