I'm building a PWA that I want to work fully offline after visiting the root '/' page of the site. There are three pages that I want to have access to offline (including all their dependencies).
I've set up preCacheAndRoute and it seems to add the pages to the cache (I can see them) and the header seems to be OK:
When I switch the server to offline mode and then visit /audit/edit (via an A tag click from '/') workbox says it cant find the file in cache, then tries the network, fails.. and then errors out.
service_worker.js.erb (JS with a small amount of ruby to get the path to fingerprinted files)
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim());
});
importScripts("https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js")
const {CacheFirst, NetworkFirst, StaleWhileRevalidate, CacheOnly} = workbox.strategies;
const {registerRoute, setCatchHandler} = workbox.routing;
const {warmStrategyCache} = workbox.recipes;
const {precacheAndRoute} = workbox.precaching;
const {CacheableResponsePlugin} = workbox.cacheableResponse;
<% controllers = Dir.glob('app/javascript/controllers/**/*.js') %>
precacheAndRoute([
{url: '/', revision: null},
{url: '/audits/edit', revision: null},
{url: '/templates/list-detail-index', revision: null},
{url: '/templates/list-detail-pdf', revision: null},
{url: '/templates/defect-card-edit', revision: null},
{url: '/templates/defect-card-pdf', revision: null},
{url: '/templates/defect-staged-image', revision: null},
{url: '<%= asset_path('application.js') %>', revision: null},
{url: '<%= asset_path('application.css') %>', revision: null},
{url: '/assets/utils/indexedDB', revision: null},
{url: '/assets/services/TemplateService', revision: null},
{url: '/assets/gateways/ClixifixAPI', revision: null},
{url: '/assets/utils/FileUtils', revision: null},
{url: '/assets/services/PDFService', revision: null},
{url: '<%= asset_path("exifr.js") %>', revision: null },
{url: '/assets/services/ExifService', revision: null},
{url: '/manifest.json', revision: null},
],
{
// Ignore all URL parameters.
ignoreURLParametersMatching: [/.*/],
directoryIndex: null,
}
);
registerRoute(
({url}) => ['/', '/audits/edit',].includes(url.pathname),
new NetworkFirst({
cacheName: 'forms-home'
})
);
registerRoute(
({request}) => ['script', 'style', 'manifest', 'font'].includes(request.destination),
new NetworkFirst({
cacheName: 'assets-styles-scripts',
})
);
registerRoute(
({request}) => request.destination === 'image',
new NetworkFirst({
cacheName: 'assets-images'
})
);
registerRoute(
({request, url}) => !['/audits/edit', '/audits/edit/'].includes(url) && request.destination === "document" ||
request.destination === "",
new NetworkFirst({
cacheName: 'documents',
})
)
The service worker is loaded by a companion/js file
if (navigator.serviceWorker) {
navigator.serviceWorker.register("/service-worker.js", { scope: "/" })
.then(() => navigator.serviceWorker.ready)
.then(() => console.log("[Companion]", "Service worker registered!"));
}
The templates that are precached are requested via the same mechanism and seem to work OK (accessed via '/').
Ive gone round in circles with this for days now so I'd appreciate any help
It looks like, in my case, this was really a rails issue.
Rails was generating fingerprinted files via the asset pipeline (including for importmap referenced files). Generating the manifest (without workbox, required usage of the asset manifest to get the fingerprinted file names).
My current, working solution is to set up the service worker to include:
const {PrecacheController} = workbox.precaching;
const precacheController = new PrecacheController();
precacheController.addToCacheList([
{ url: '/', revision: null },
{ url: '/audits/edit', revision: null },
{ url: '/audits/show', revision: null },
<% Rails.application.assets_manifest.assets.each do |key, value| %>
{ url: '<%= asset_path(key) %>', revision: null },
<% end %>
I also have to precompile assets and refresh importmaps before I serve.
Another gotcha is the browser cache. In development i'd recommend switching this off.