Example Project address: github.com/zColdWater/… Download the Demo and work with it to make it easier to understand.

First, preparation

Before we begin, we need to clarify some of the following questions for the rest of our discussion.

1. Scope of URLRequest

When we talk about URLRequest, I’m sure a lot of developers in China will think, first of all, HTTP requests, and then nothing else. However, URLRequest is a big concept, it not only serves HTTP protocol, it also serves other application protocols, such as File protocol, Data protocol, custom protocol and so on. Or why didn’t Apple call it HTTPURLRequest? The problem is that HTTP is the protocol we most often use to request data from the server for presentation.

2. Scope of URLRequest.CachePolicy

URLRequest has a number of protocols. What is the scope of URLRequest.CachePolicy? Of course, I don’t know what the caching strategy is for other protocols, like File, or whatever. But I do know that the caching protocol that we use for HTTP, and I’ll talk about that later, but what’s clear here is that this caching policy supports a lot of protocols, and our HTTP protocol has its own caching policy.

3. HTTP cache policy

Before I turned a Taiwan author caching policy articles about the HTTP protocol, this paper addresses are: http://47.99.237.180:8080/articles/2019/11/18/1574050998351.html and HTTP caching policy is what?

Note: First of all, we need to be clear that the HTTP protocol strategy requires the client and server to cooperate. For this strategy to work, both sides need to act and the client needs to cooperate fully.

Let me summarize the principle in the most straightforward terms:

  • First, the client accesses a resource on the server for the first time, and the server and client agree to use the standard HTTP caching protocol.

  • For the first time, the client searches through the URL, finds that there is no local cache, and sends an HTTP request to the server. Client request headers, for example:

    HTTP/1.1 Host: fep-sit.nioint.com:5418 Accept: */* user-agent: SessionDownload/1 CFNetwork/1107.1 Darwin/19.0.0 Accept-language: en-us accept-encoding: gzip, deflate Connection: keep-aliveCopy the code

    This is a normal network request, and the request header is the client’s default, with no special Settings.

  • Server received a request from the client, then do directly take a look at this request header did provide HTTP cache related fields, and then according to the rules of the HTTP protocol buffer, to judge whether to return a status code of 304 let the client to use caching, or return a status code of 200, the client with the server to return the new resources, The server checks the following fields in the request header:

    • If-None-Match: W/” 20f9A-16f5C76370d “: W/” 20f9A-16f5C76370d” : W/” 20f9A-16f5C76370d “: W/” 20f9A-16f5C76370d” : W/” 20f9A-16f5C76370d “: W/” 20f9A-16f5C76370d” : W/” 20f9A-16f5C76370dThe serverIn the header of the response returnedEtagField. Since our client is requesting for the first time, we did not get this value from the previous server response, so the request header does not have this field.
    • If-Modified-Since:Tue, 31 Dec 2019 14:57:28 GMT This field represents the time when the resource was last changedIf-None-MatchAlso from the last server response header, fromLast-ModifiedField. Because of the first request, the field in the previous response header was not retrieved and therefore not carried.
  • The server starts checking the HTTP protocol rules to decide whether to let the client use the cache or use the resources delivered by the server.

    • The server has two types of response headers:
      1. Status code 200 (tell client, don’t use cache, use the new resource I give you)
         HTTP/1.1 200 OK  
         X-Powered-By: Express  
         Accept-Ranges: bytes  
         Cache-Control: public, max-age=0  
         Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT  
         ETag: W/"20f9a-16f5c76370d"  
         Content-Type: image/gif  
         Content-Length: 135066  
         Date: Wed, 01 Jan 2020 01:56:35 GMT  
         Proxy-Connection: keep-alive
        Copy the code
      2. Status code 304 (Tell the client to use your own cache)

        Note that in iOS, you don’t have to deal with 304 yourself. If you use the default caching policy, which is HTTP itself, the system’s network framework like URLSession or URLConnection will automatically process 304 to 200. This is easy for developers to handle logic, developers only need to know that the resource was successful, it is ok.

        HTTP/1.1 304 Not Modified
        X-Powered-By: Express
        Accept-Ranges: bytes
        Cache-Control: public, max-age=0
        Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT
        ETag: W/"20f9a-16f5c76370d"
        Date: Wed, 01 Jan 2020 01:59:25 GMT
        Proxy-Connection: keep-alive
        Copy the code
    • HTTP cache policy fields in the server response header:
      1. Cache-Control: In other words, if the server tells the client that Max -age=100, the client will not make a real network request until 100 seconds. The client network layer framework will automatically return status code 200, the last cached data)
      2. Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT: Tue, 31 Dec 2019 14:57:28 GMT: Tue, 31 Dec 2019 14:57:28 GMT: Tue, 31 Dec 2019 14:57:28 GMT: Tue, 31 Dec 2019 14:57:28 GMT: Tue, 31 Dec 2019 14:57:28 GMT: Tue, 31 Dec 2019 14:57:28 GMTIf-Modified-SinceThe rule here is that if you ask for theIf-Modified-SinceThe time point is earlier than the serverLast-ModifiedAt this point in time, the server will return 200, so that the client needs to update the latest resource, if the reverse, or the same, the server will issue 304, so that the client can use the cache.
      3. ETag: W/” 20f9A-16f5C76370d “(this field tells the client that this value is the unique ID of the resource. If there is a new resource on the server, we will update this value. The client should save this value for the next requestIf-None-MatchThe field is what was in the header of the last response savedEtagField. The rule is that if the client requests a headerIf-None-MatchValues do not correspond to those inside the serverEtagIf the value is consistent, 200 is returned to allow the client to use the new resource. If the value is equal, 304 is returned to allow the client to use the cache.
  • The server checks the request header and finds that the client does not carry the resource cache information. The server assumes that the client does not want to use the HTTP protocol cache policy and returns 200 with the resource.

    HTTP/1.1 200 OK
    X-Powered-By: Express
    Accept-Ranges: bytes
    Cache-Control: public, max-age=0
    Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT
    ETag: W/"20f9a-16f5c76370d"
    Content-Type: image/gif
    Content-Length: 135066
    Date: Wed, 01 Jan 2020 01:56:35 GMT
    Proxy-Connection: keep-alive
    Copy the code
  • Cache-control: cache-control: cache-control: cache-control: cache-control: cache-control: cache-control: Public, max-age=0 read max-age=0, which tells me that I should send a real request every time, and then store last-Modified when it was Last updated, and then store ETag, which tells me the unique ID of the resource.

  • The client then initiates a second request with the following header:

    GET /demo.gif HTTP/1.1
    Host: 152.136.154.126:3000
    If-None-Match: W/"20f9a-16f5c76370d"
    Accept: */*
    If-Modified-Since: Tue, 31 Dec 2019 14:57:28 GMT
    User-Agent: SessionDownload/1 CFNetwork/1107.1 Darwin/19.0.0
    Accept-Language: en-us
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    Copy the code

As you can see, the client is bringing information about the cached resource in if-none-match, if-modified-since.

  • The serverAfter receiving the request header again and comparing it with its own information on the resource, it finds If-None-MatchandETagConsistent, foundLast-ModifiedandIf-Modified-SinceI should tell the client the 304 status code and let the client use the cache. The response header is as follows:
    HTTP/1.1 304 Not Modified
    X-Powered-By: Express
    Accept-Ranges: bytes
    Cache-Control: public, max-age=0
    Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT
    ETag: W/"20f9a-16f5c76370d"
    Date: Wed, 01 Jan 2020 03:02:57 GMT
    Proxy-Connection: keep-alive
    Copy the code

At this point, a complete HTTP caching policy is applied, and we can see that the client initiates a request for the first time, and then initiates a request for the second time. And how the server responds to the checksum of the request header.

I know there are Expire fields, which are also cache-dependent, but HTTP1.1 sets max-age in cache-control to override them if they exist at the same time, so I won’t outline them here. If you are interested, you can WIKI, Or http://47.99.237.180:8080/articles/2019/11/18/1574050998351.html

Hopefully I can clarify this process, because the web framework below iOS will be involved. This is the HTTP caching strategy, which means that any client that uses HTTP, whether it’s a browser, mobile, or whatever, uses it.

It doesn’t matter if it’s not very clear right now, but I’ll include a Demo of the whole process in the article, and you can run the Demo to see what each step is.

4. There was an incident

Except for a few hicks during the validation process, for example, I’ve set the expiration time for static resources on the server. In the response header, cache-control: Max – age = XXX, found on the mobile terminal, is OK, for XXX seconds to visit again, really didn’t have a network request, but I found in the middle of the Chrome address input resource directly, and it will ignore the expiration time, will be a real network request, I look at the small half-day, I finally found out, this what is the problem, So let me talk about it.

  1. On the mobile terminal: The default URLRequest cache policy is used. The cache policy uses the cache policy of the protocol. The timeout period works properly.
  2. Web: YesThe labelorXHRThe setting of the timeout period for the network request works properly.
  3. Web: When the browser is used to access resources, the server sends network requests even if the timeout period is set.

To sum up: Chrome and Safri have different behavior, but only 3 is displayed incorrectly. The problem may be that the user directly enters the resource address in the browser to access the resource, ignoring the timeout period. Therefore, if we want to verify HTTP caching strategy, we can ignore 3 and verify 1 and 2.

On iOS, URLRequest.CachePolicy and URLCache

We’ve covered a few things you need to know beforehand, such as the scope of URLRequest and urlRequest.cachePolicy in iOS and, most importantly, the most complex HTTP caching policy.

0. Relationship between CachePolicy and URLCache

CachePolicy: As the name implies, this stands for caching policy, which I’ll talk about in more detail below, as long as we know it represents a caching policy.

URLCache: This is actually the object that we cache, that we use some kind of caching strategy to store where the content is stored, where the configuration is stored, etc., or the object that manages the URL cache.

Summary: CachePolicy is used to select a CachePolicy, and URLCache manages and sets cache objects.

1. Where can I set a CachePolicy

Let’s start by looking at what policies are available for CachePolicy:

  • useProtocolCachePolicy// Use protocol caching, such as yoursURLisHTTPBy agreement, that is to useHTTPThe caching strategy of the protocol, as I mentioned above, is to cache the request and response headers based on their keywords.This is also (the default policy).
  • reloadIgnoringLocalCacheData// Ignore the local cache completely and fetch resources directly from the server.
  • reloadIgnoringLocalAndRemoteCacheData// This instance is unimplemented and you should not use it
  • reloadIgnoringCacheData/ / this option has been replaced by a reloadIgnoringLocalCacheData option.
  • returnCacheDataElseLoad// If there is a local cache, use the cache, regardless of the HTTP protocol cache max-age, expire time, etc. The problem with this protocol is that if you use HTTP caching, the server has no way of telling it that the data it has is old.
  • returnCacheDataDontLoad// If there is a local cache, use the cache. If there is no local cache, the request fails.
  • reloadRevalidatingCacheData// This instance is unimplemented and you should not use it.

There is nothing wrong with apple’s default policy. It is best to configure a cache based on the HTTP protocol. If there is a cache, use the cache, no cache, or the cache expires, to request resources from the server. Or you simply don’t have to go to Settings, MAO, but we are often asked why I server in the image, you move the end iOS update, if you are a default policy, I can be very responsible to tell you, you can hard Dui back, do you understand not understand HTTP caching strategies, I this is the official implementation, instead, unknown reason, wei-wei, good, I’m going to get everything from the server instead. If you don’t know the HTTP protocol, you might think you’re not good at it, but to be honest, a lot of people don’t really understand HTTP caching.

Where can I set the URLRequestCache policy? In fact, you should know from the name that this thing is definitely for URLRequest, so EVERY time I make a web request, I get a URLRequest, but for convenience, Apple has two places to set this policy for you:

  • URLSessionConfiguration: All passURLSessionsentURLRequestI bring this strategy with me.
            let url = URL(string: "http://152.136.154.126:3002/demo.gif")!
            var request = URLRequest(url: url)
            let condig = URLSessionConfiguration.default
    
    	// this this this ⬇️ this this ⬇️ this this this ⬇️ this this this ⬇️
            condig.requestCachePolicy = .reloadIgnoringLocalCacheData
            
            let session = URLSession(configuration: condig)
    Copy the code
  • URLRequest: Set thisURLRequestCache strategy, this is nothing to say.
    let url = URL(string: "http://152.136.154.126:3002/demo.gif")!
    var request = URLRequest(url: url)
    
    // this this this ⬇️ this this ⬇️ this this this ⬇️ this this this ⬇️
    request.cachePolicy = .returnCacheDataDontLoad
    
    let condig = URLSessionConfiguration.default
    let session = URLSession(configuration: condig)
    Copy the code

Q: 🤔️? At the same time,URLSessionCondigurationURLRequestWhich cache strategy should the system use?

Answer A:URLRequestThe cache policy will overrideURLSessionCondigurationCache policy.

2. Where can YOU set URLCache

Distinguish URLCache and NSCache nshipster.com/nsurlcache/ the two things are not the same thing ah, see the name as you know, is a special cache of the URL class, one is similar to the Map, dictionary storage structure, used to cache memory object.

The URLCache object is used to manage the cache of URLS, such as where to put them and how big to set them.

Where can I set it? There are two places to set it:

  • URLSessionConfiguration: The URLSession is configured with a cache object, size, path, etc. If the URLSessionConfiguration is configured with a cache object, all URlRequests sent by the URLSession will use the cache configuration instead of the global cache object.

    let condig = URLSessionConfiguration.default
    condig.requestCachePolicy = .reloadIgnoringLocalCacheData
    
    let cache = URLCache(memoryCapacity: 4 * 1024 * 1024, diskCapacity: 20 * 1024 * 1024)
    condig.urlCache = cache
    Copy the code
  • URLCache: Class methods provide a global URLCache configuration and use the global cache as their own when URLSessionCondiguration is not set otherwise.

    URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
    Copy the code

Q: 🤔️? If you set bothGlobal cacheandURLSessionCondigurationCaches, what caches does the system have?

A: YesURLSessionCondigurationGlobal cache is ignored.

3. Several special scenarios

  1. URLSessionConfiguration Cache object with space set to 0.

    let url = URL(string: "http://152.136.154.126:3002/demo.gif")!
    var request = URLRequest(url: url)
    let condig = URLSessionConfiguration.default
    
    // Set the cache for this Session separately, and the space is 0, because the default cache policy is used, i.e. the 'HTTP' cache, but the local cache has no space, so the latest data is fetched from the server every time.
    let cache = URLCache(memoryCapacity: 0, diskCapacity: 0)
    
    let session = URLSession(configuration: condig)
    let task = session.dataTask(with: request) { (data, resp, error) in
        let httpResp = resp as! HTTPURLResponse
        print("response (StatusCode):\(httpResp.statusCode)")
        DispatchQueue.main.async {
            let httpResponse = resp! as! HTTPURLResponse
            for (key,value) in httpResponse.allHeaderFields {
                print("\(key):\(value)")}self.imageView.image = UIImage.gifImageWithData(data: data! as NSData)
        }
    }
    task.resume()
    Copy the code
  2. URLCache Global cache object with space set to 0.

    // The URLSessionCondiguration uration is set to a global cache. The global cache is set to 0, and the default cache policy is to fetch the latest data from the server every time.
    URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
    let url = URL(string: "http://152.136.154.126:3002/demo.gif")!
    var request = URLRequest(url: url)
    let condig = URLSessionConfiguration.default
    let session = URLSession(configuration: condig)
    let task = session.dataTask(with: request) { (data, resp, error) in
        let httpResp = resp as! HTTPURLResponse
        print("response (StatusCode):\(httpResp.statusCode)")
        DispatchQueue.main.async {
            let httpResponse = resp! as! HTTPURLResponse
            for (key,value) in httpResponse.allHeaderFields {
                print("\(key):\(value)")}self.imageView.image = UIImage.gifImageWithData(data: data! as NSData)
        }
    }
    task.resume()
    Copy the code

The request cache in WKWebView and UIWebView.

The thing about WebView is, the cache policy you set is the cache policy for the tags in the page, which is, even though you set the cache policy is: Don’t use local caching, just HTML loaded tags that use this policy, such as , but if XHR or Fetch requests from HTML JS still use the default cache policy, you won’t change to them. This is what I verified with network interception, and the Demo is at the beginning and end of the article.

Interceptor project address: github.com/zColdWater/… .

One, if you want the site to use the default caching strategy for all tags, you don’t need to set it differently. This is a caching strategy using HTTP.

let url = URL(string: "https://www.baidu.com/")
var request = URLRequest(url: url!)
webview.load(request)
Copy the code

Second, if you want the site’s resource tags to be requested using a specific caching strategy, you can do so.

let url = URL(string: "https://www.baidu.com/")
let request = URLRequest(url: self.url! , cachePolicy: .reloadIgnoringLocalCacheData) webview.load(request)Copy the code

Summary: If the article is not intuitive, you can download the Example project and run it.

Four,

In fact, for iOS URLRequest caching, Apple’s default policy is a very reasonable choice, but in order to use this default policy properly, you need to understand the HTTP protocol caching policy, for WebView caching policy we also explained, I hope to help those who are not clear about this cache policy before. Don’t run into a problem and just set it to not use caching. Using caching wisely is the best option.

I hope I can say clearly, if there is inaccurate or wrong place, I hope you correct.

Example Project address: github.com/zColdWater/… Download the Demo and work with it to make it easier to understand.

Reference: blackpixel.com/writing/201…