WKWebView - What configuration is needed to honour cache control headers?

Hi,


I am loading a resource from a remote server and rendering its content in a WKWebView instance. I am configuring URLRequest object with a cachePolicy of .useProtocolCachePolicy and using the load(request:) method on the webview instance.


The remote resource has a response header of Cache-Control: max-age=31536000. Yet the webview does not honour this cache policy. It always redownloads the remote resource.


What other configurations are needed to ensure the resource is cached and persisted to disk for future cached loading? I see WKWebView uses a WKWebsiteDataStore object to persist cache. Is there something I need to configure there to get it to cahce correctly?


Am I right in saying that WKWebView does not use the shared URLCache?


Thanks!

Replies

Am I right in saying that

WKWebView
does not use the shared
URLCache
?

No. Web views do cache but, as with all web caching, there’s no guarantee that they will cache a specific resource. I’m not sure why the web view hasn’t cached the resource you care about — I expect it’d make sense if you drilled down into the caching logic — but ultimately the decision as to what gets cache is the web view’s to make.

If you require the web view to cache a specific resource, that’s no longer a cache but something more like an offline mode, and the web views do not support that in this way.

Can you explain more about why you require this resource to be cached?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Eskimo.


I am developing an enterprise application where we are loading a web application into a WKWebView. For performance reasons, I would like the webview to cache resources that have cache control response headers. Resources such as index.html in a single page application for example.


Due to some limitations out of my control, I need to bundle the entire web application into index.html so it is effectively one asset being downloaded which has a cache-control header with a large max-age in seconds. This is not for offline viewing, but for faster load times of the web application on subsequent loads.


Since you replied to me I have been able to figure out why the the application wasn't being loaded from cache. There is a dynamic query string in the request leading to the browser thinking it is a different endpoint. I have noticed that it loads the web application from disk cache on first load of the web app into WKWebView. But if you try to reload the web applciation into the webview within the application, it does not load from either disk or memory cache, it loads from the network.


Do you know why this would be the case?


Regards,

Fergal.

Most people have the opposite problem. WKWebView caches too much. Your dynamic query string is the most reliable trick to get WKWebView to force a re-download and avoid the cache. You seem stuck between a rock and a hard place here.


However, you did mention "webview within the application". Since you do have everything inside one file, have you considered loading your webview directly from an HTML string? Are you sure you have the entire application in one file? Images too? If you did have additional files, you could use WKURLSchemeHandler to load them directly from memory too.

But if you try to reload the web applciation into the webview within the application

Reload by calling

load(request:)
? Or reload by calling
reload
?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks John.


I will be removing the query string to allow for client side caching so have removed myself from between the rock and a hard place 🙂


The web application is remote and not local so not sure where loadHTMLStirng would come into play here. Your tip on WKURLSchemeHandler is a great one as this may help me overcome the limitations I have with the browser requesting assets such as JS and CSS for example.

I tried both

load(request:)
and
reload.

You can always pull the HTML directly (and other resources), not within the web view, and then display it as a string.


Unfortunately, the new "WK" web views are still missing significant functionality compared to the old "legacy" web views. On iOS, Apple will no longer even accept apps built using the legacy web views. But on macOS Catalina, the "legacy" web views are required to print.


Sadly, if you want to use Apple's APIs, in many cases, you simply have to beat it with a rubber hose to make it work. Be creative. Don't have mercy. You can write bug reports if you want, but don't sit on your hands and wait for them.

On iOS, Apple will no longer even accept apps built using the legacy web views.

This is not true. The app ingestion process currently issues a deprecation warning if you use

UIWebView
but it’s not actually blocked, and there’s been no announcement as to when that will change.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

It wouldn’t surprise me if

reload
was triggering a… well… reload. The
load(request:)
case is more interesting.

If you set up a test page with the same cache properties but that’s much smaller resource, does it have the same problem?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Does WKURLSchemeHandler support intercepting http/https requests? Doesn't look like it from reading up on this. Think its custom protocols only?

Hey Eskimo,


I tried as you suggested and seeing the same issue. Tested a 12KB html file with 'Cache-Control: max-age=36000000'. Loads from disk on startup, but on subsequent reloads in app, it loads from the network each time.


Should I log a bug for this?

fergieee wrote:



Does WKURLSchemeHandler support intercepting http/https requests? Doesn't look like it from reading up on this. Think its custom protocols only?

No. It is only for custom protocols. One of the common complaints about WKURLSchemeHandler is that it doesn’t support intercepting http and https.


In my case, I have a custom protocol “estp:” that reads data directly from a local cache. You can specify a baseURL for resources and that baseURL can use your custom protocol. Then you never need to explicity refer to the custom protocol in your HTML.


I used to include an embedded web server in the app. It sure felt good to remove all of that code, but I can't honestly say it was worth it. Maybe it will be better for iOS.

eskimo wrote:



This is not true. The app ingestion process currently issues a deprecation warning if you use UIWebView but it’s not actually blocked, and there’s been no announcement as to when that will change.

This is one of those times where truth is relative. This is not a trivial change. Someone doing advanced work with web views is going to have a major re-engineering effort ahead of them. In some cases, they will lose functionality. Anyone currently using UIWebView, or its equivalent on macOS, needs to start this re-engineering effort immediately. That way, they can research workarounds, such as embedded web servers, or even file bug reports. It would be a very bad idea to wait until apps start crashing, apps can't be submitted at all, or even an offical announcement. While there is no way to avoid letting Apple dictate a development schedule, one can, at least, try to prevent it from becoming a crisis.

Tested a 12KB html file … it loads from the network each time.

OK. Internally,

WKWebView
uses
NSURLCache
, and that definitely limits the maximum size of resources that it’ll cache. Still, I’d expect 12 KB to be cached.

If you write a simple

NSURLSession
client that requests the same resource repeatedly, does it cache?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I wrote an NSURLSesstion client as suggested using a dedicated URLCache and useProtocolCachePolicy in the URLSessionConfiguration. The html file successfully loaded from cache each time, both on startup and in app reloads.