I created a HTTPEntityFingerprint structure which stores some of the entity headers: Content-MD5
, Etag
, and Last-Modified
.
import Foundation
struct HTTPEntityFingerprint {
let contentMD5 : String?
let etag : String?
let lastModified : String?
}
extension HTTPEntityFingerprint {
init?(response : NSURLResponse) {
if let httpResponse = response as? NSHTTPURLResponse {
let h = httpResponse.allHeaderFields
contentMD5 = h["Content-MD5"] as? String
etag = h["Etag"] as? String
lastModified = h["Last-Modified"] as? String
if contentMD5 == nil && etag == nil && lastModified == nil {
return nil
}
} else {
return nil
}
}
static func match(first : HTTPEntityFingerprint?, second : HTTPEntityFingerprint?) -> Bool {
if let a = first, b = second {
if let md5A = a.contentMD5, md5B = b.contentMD5 {
return md5A == md5B
}
if let etagA = a.etag, etagB = b.etag {
return etagA == etagB
}
if let lastA = a.lastModified, lastB = b.lastModified {
return lastA == lastB
}
}
return false
}
}
When I get an NSHTTPURLResponse
from an NSURLSession
, I create an HTTPEntityFingerprint
from it and compare it against a previously stored fingerprint using HTTPEntityFingerprint.match
. If the fingerprints match, then the HTTP resource hasn't changed and thus I do not need to deserialized the JSON response again; however, if the fingerprints do not match, then I deserialize the JSON response and save the new fingerprint.
This mechanism only works if your server returns at least one of the 3 entity headers: Content-MD5
, Etag
, or Last-Modified
.
More Details on NSURLSession and NSURLCache Behavior
The caching provided by NSURLSession
via NSURLCache
is transparent, meaning when you request a previously cached resource NSURLSession
will call the completion handlers/delegates as if a 200 response occurred.
If the cached response has expired then NSURLSession will send a new request to the origin server, but will include the If-Modified-Since
and If-None-Match
headers using the Last-Modified
and Etag
entity headers in the cached (though expired) result; this behavior is built in, you don't have to do anything besides enable caching. If the origin server returns a 304 (Not Modified), then NSURLSession
will transform this to a 200 response the application (making it look like you fetched a new copy of the resource, even though it was still served from the cache).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…