I've been struggling the last couple of days with loading a new PWA version in my Angular 11 app. Starting to pull my hair out!
This is not happening for me:
"Every time the user opens or refreshes the application, the Angular service worker checks for updates to the app by looking for updates to the ngsw.json manifest. If an update is found, it is downloaded and cached automatically, and will be served the next time the application is loaded."``` Angular - Service worker in production
I'm using the Angular service SwUpdate.checkForUpdate()
which works great, and detects and prompts the user to reload when a new version becomes available. The app is reloaded and is on the new version, which is perfect.
However, if I close the app on my phone, and re-open it always goes back to the first version when the PWA was installed :(
I've tried SwUpdate.activateUpdate()
window.location.reload()
, changing version numbers, custom service worker... etc. etc.
The only thing that works is to clear my browsing data in Chrome, and re-install the PWA :(
There's nothing that I can do which will cause the PWA cache to be updated with the new version. It is getting the new version, but the cache is not being updated.
Can someone point me in the right direction here? What am I missing?
Android 10, Chrome 89.0.4389.105
The main issue was a long 30-day max-age expiry on the index.html. It seems to me that Chrome on Android loads from the standard browser cache first before hitting the pwa cache.
This was not happening in Chrome desktop, just Android from what I tested.
I resolved this issue by:
Additional steps, may be unnecessary depending on your situation:
ngsw.config.json
to force to load from the network.Custom service worker, in app.module.ts
ServiceWorkerModule.register('./offline-service-worker.js', { enabled: environment.production }),
offline-service-worker.js
file:
const CACHE_NAME = 'offline';
const OFFLINE_URL = '/assets/offline-page.html';
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
const cache = await caches.open(CACHE_NAME);
await cache.add(new Request(OFFLINE_URL, {cache: 'reload'}));
})());
});
self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
if ('navigationPreload' in self.registration) {
await self.registration.navigationPreload.enable();
}
})());
self.clients.claim();
});
self.addEventListener('fetch', (event) => {
if (event.request.mode === 'navigate') {
event.respondWith((async () => {
try {
// First, try to use the navigation preload response if it's supported.
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// catch is only triggered if an exception is thrown, which is likely
// due to a network error.
// If fetch() returns a valid HTTP response with a response code in
// the 4xx or 5xx range, the catch() will NOT be called.
console.log('Fetch failed; returning offline page instead.', error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse;
}
})());
}
});
importScripts('./ngsw-worker.js');
This whole PWA is a tricky thing. But great when it works!