13

I am trying to enable eTag support in my app. I am using Alamofire 4 in my swift 3 project.

It seems that eTag is transparently handled by URLRequest which Alamofire uses:

NSURLCache and ETags

But it doesn't work.

Here is http header sent by web server:

headers {
    Connection = "keep-alive";
    "Content-Length" = 47152;
    "Content-Type" = "application/json";
    Date = "Tue, 06 Dec 2016 22:43:18 GMT";
    Etag = "\"ecf38288be2f23f6710cafeb1f344b8c\"";
} })

Do yo have any hint?

Thanks a lot.

Community
  • 1
  • 1
thierryb
  • 3,660
  • 4
  • 42
  • 58

2 Answers2

8

By default, caching is ON. If you log HTTP traffic inside your app, you might see cached responses, without the app making requests to server this time.

In case when URLSession decides to return cached response instead of going to server you will see same Date HTTP response header.

To ensure caching is working, you should inspect network packets between your app and server.

paiv
  • 5,491
  • 22
  • 30
  • I just looked at [this](https://docs-assets.developer.apple.com/published/d24b133ea3/http_caching_2x_820a949f-9d5d-4a85-9ca2-50b42b339e18.png) image from [here](https://developer.apple.com/documentation/foundation/nsurlrequest.cachepolicy). It seems for this to work the client MUST automatically make a quick `HEAD` request. My question is what happens if your server doesn't route/implement `HEAD` requests? Does that mean it would not work at all and basically every time you would actually be making `GET` requests—regardless of setting cachePolicy: `NSURLRequestUseProtocolCachePolicy`? – mfaani Jul 14 '17 at 10:05
  • @Honey on your image, there is a path where HEAD is not required. – paiv Jul 14 '17 at 12:37
  • ...until response is stale. Only when it's stale, HEAD will be issued. If server doesn't handle HEAD, resource would be marked as changed. You should debug this by capturing HTTP packets to be sure. – paiv Jul 14 '17 at 12:49
  • Thanks 1. What's the default timer set for stale? 2 minutes? 10 minutes? 1day? 2. What happens if the actual data has changed 10 times, but we haven't yet hit our the stale/expiration time?! – mfaani Jul 14 '17 at 16:26
  • 3. I created a sample project and I'm trying to test the behavior. I created a `URLRequest` and set its cache policy to `.useProtocolCachePolicy` and then inside my `session.dataTask(with: request as URLRequest) { (data, response, error) in` I printed the `print(httpResponse.statusCode)` & `print(httpResponse.allHeaderFields)` for my repeating requests. The `statusCode` is always `200` which I guess means there is no caching right? Also I don't know much of how to make sense of the Headers. Is there any specific key that I should look into? – mfaani Jul 14 '17 at 16:26
  • To answer myself: 1. The default is set my the server 2. If changes 10 times but you haven't hit your expiration time, then you won't get updated. Caching isn't perfect. Having that said there is a [**caching busting**](https://www.keycdn.com/support/what-is-cache-busting/) approach that can help you trigger a fresh download. It's a hack! 3. Yes you will always get the same response—because the response and the data *are* cached. Even if you turn wifi off, you'll still get 200 (which is one of testing) Another way of testing is: see if the `date` from the headerResponse has changed or not. – mfaani Jul 16 '17 at 20:27
1

This is the default cache policy when using an URLRequest.

If you want to change this behavior and see the "real" response from the network, so that you can handle the eTag and the statusCode by yourself, you just need to change the cache policy to reloadIgnoringCacheData:

var exampleRequest = try! URLRequest(url: route, method: .post, headers: headers)
// This is the important line.
exampleRequest?.cachePolicy = .reloadIgnoringCacheData

Hope this helps someone!

  • how can you then *change* the behavior? Aren't you just suggesting to ignore cache entirely? And just download no matter what?! – mfaani Sep 11 '17 at 14:54
  • I think it depends on your use case. If your backend (or, in general, the API you're calling) always answers 200 with the full response, then I agree with you that you need to keep caching on. But if your backend implemented HTTP-Conditional-Get (so that it answers 200 with the full body if there's been any changes and *304* with empty body if there hasn't been any), like in my case, and you need to access to the real statusCode of the response (because you need to do some specific actions in that case), you need to change the `URLRequest` cachePolicy. – Davide Castello Sep 11 '17 at 15:15
  • If I understand correctly, setting the cachePolicy to reloadIgnoringCacheData will give you 304 and no JSON response if nothing has changed based on the etag header for your request. But if Alamofire and URLSession are handling the caching automatically, why is it that I get 200 if I use the default cache policy? – SleepNot Aug 14 '19 at 06:13
  • That's because Alamofire will handle the cache by itself and won't let you see the real response hitting the network. If you want to use HTTP-Conditional-Get, from what I've seen you need to set the cachePolicy to reloadIgnoringCacheData and handle the status code of the response by yourself. – Davide Castello Sep 05 '19 at 08:52