Why did I need to set the cache policy on the URLRequest when it was already set on the URLSession to avoid caching?

236 Views Asked by At

I am building an iOS app that needs to frequently check for changes to a JSON file on my server. In the process of doing this I discovered a quirk with caching. My server is running a very basic http-server for now as I get things off the ground.

When I realized caching was preventing my clients from writing At first I wrote the code:

let urlSession = URLSession(configuration: .ephemeral)
func updateModel() {
  let (data, _) = try await urlSession.data(from: URL(string: myURLString)!)
  // Do something with the data
}

Which was suggested in the Apple developer forum.

This did not work so I went a step further trying this

let urlSession =  {
    var session = URLSession(configuration: .ephemeral)
    session.configuration.urlCache = nil
    session.configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
    return session
}()
func updateModel() {
  let (data, _) = try await urlSession.data(from: URL(string: myURLString)!)
  // Do something with the data
}

This approach was suggested in a Stack Overflow answer here. Worth noting people in the comments mentioning it didn't work for them or they needed to call URLCache.shared.removeAllCachedResponses() which isn't ideal. I didn't want to do that call and this code was not working for me so I went onto something much heavier handed.

let urlSession =  {
    var session = URLSession(configuration: .ephemeral)
    session.configuration.urlCache = nil
    session.configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
    return session
}()
func updateModel() {
  let request = URLRequest(url: URL(string: myURLString)!, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData)
  let (data, _) = try await urlSession.data(for: request)
  // Do something with the data
}

Which I don't know precisely where I got the idea to try from.

So I guest. Yay! It works. But also... why was all this necessary? Why did I need to nix the cache both at the URLSession AND at the URLRequest level? Does it have to do with my use of a string URL, maybe the way my server is vending resources, maybe iOS 17, the content type I am vending. I would like to understand more.

1

There are 1 best solutions below

0
Elevo On

Let us see the func we called.

extension URLSession {

    public func data(for request: URLRequest, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse)

    public func data(from url: URL, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse)
}
public struct URLRequest : ReferenceConvertible, Equatable, Hashable, Sendable {

    public init(url: URL, cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, timeoutInterval: TimeInterval = 60.0)
}

For the second func of URLSession, its implementation may be

public func data(from url: URL, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) {
    let request = URLRequest(url: url)
    return self.data(from: request)
}

you guess what? the URLRequest's cachePolicy is set default to useProtocolCachePolicy.

So that's what your problem comes.