javascriptsafariservice-worker

UpdateViaCache doesn't work with Safari [Service Worker]


I'd like my application to make as few requests to the server as possible. In this context, I use updateViaCache : “all” when registering my worker. On Android devices, I don't get any requests at all. On iOS, the application makes a request for the Worker each time a new page is loaded. The problem seems to come from the browser, Safari in this case. I read this thread from 2018: https://bugs.webkit.org/show_bug.cgi?id=187435 It concludes as follows:

In any case, the patch that is about to land should align our behavior with Chrome and you should still only one serviceworker.js load per page load.

As I mentioned earlier, you can opt into using the disk cache so that your server will be hit only when the serviceworker.js script in the HTTP cache has expired. To do so:

navigator.serviceWorker.register(serviceWorkerUrl, { updateViaCache: "all" });

This is on registration, unlike what you stated earlier.

Which suggests that it should work, but it doesn't seem to.

Here's how I register my Service Worker:

async function registerServiceWorker() {
    try {
        const registration = await navigator.serviceWorker.register(
            `/service-worker.js?v=${SW_VERSION}`,
            { updateViaCache: 'all' }
        );
        if (LOG) console.log("Service Worker enregistré avec le scope :", registration.scope);
    } catch (error) {
        console.error("Erreur lors de l'enregistrement du Service Worker :", error);
    }
}

PS : SW_VERSION is used to force the worker service to be updated via the server when it is updated.


Solution

  • Safari seems to remember the URL from which the Service Worker was initially installed, and as long as you register the Service Worker with this same address, it imposes a systematic network validation, even with updateViaCache: “all”.

    Why does Safari do this?

    It's probably a security measure to avoid a scenario where a site registers a Service Worker with updateViaCache: “all”. This Service Worker would never be updated and would remain cached indefinitely, unless the URL changed.

    Safari therefore seems to have a different policy from other browsers:

    As a solution, I added these lines to the previous script :

    if (!initialRegistration) {
        await navigator.serviceWorker.register('/service-worker.js', { updateViaCache: 'all' });
    }
    

    When the client downloads the application for the first time, it doesn't yet have a registered Service Worker, so I register what I call the “initial Service Worker” to set the URL to mydomain.com/service-worker.js. Then, immediately afterwards, I register the Service Worker again (the same one) with a different URL, as this one mydomain.com/service-worker.js?v=NEW_VERSION. This process makes updateViaCache operational as soon as it's installed.