Workbox SW: Runtime caching not working until second Reload - caching

I am new to service worker and workbox. I am currently using the workbox to precache my static assets files, which works fine and I expect my other thirdparty URL to be cached too during runtime, but not working until my second reload on the page:(
Shown Below is the copy of the Code of my Service Worker, please note that I replace my original link to abc.domain.com intentionally :)
workbox.routing.registerRoute(
//get resources from any abc.domain.com/
new RegExp('^https://abc.(?:domain).com/(.*)'),
/*
*respond with a cached response if available, falling back to the network request if it’s not cached.
*The network request is then used to update the cache.
*/
workbox.strategies.staleWhileRevalidate({
cacheName: 'Bill Resources',
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
);
workbox.routing.registerRoute(
new RegExp('^https://fonts.(?:googleapis|gstatic).com/(.*)'),
//serve from network first, if not availabe then cache
workbox.strategies.networkFirst(),
);
workbox.routing.registerRoute(
new RegExp('^https://use.(?:fontawesome).com/(.*)'),
//serve from network first, if not availabe then cache
workbox.strategies.networkFirst(),
);
I have cleared storage times without number, I refreshed cache storage from google developer tools, but all seems to be the same. Resources from a custom link, google fonts and fontawesome, fail to be cached the first time. Below is the console and the Cache Storage Tab for my page first load image and the second load Image respectively.
Please I dont know what I am doing wrong and why it behaves like so.
Thanks in Advance

This is expected behaviour.
The way service workers get set up is that they will have an install and activate phase, where installation can happen when ever a new service worker is registered or a service worker updates.
A service worker will then activate when it's safe to do so (i.e. no windows are currently being "controlled" be a service worker).
Once a service worker is activated, it'll control any new pages.
What you are seeing is:
Page is loaded and the page registers a service worker
The service worker precaches any files during it's install phase
A service activates but isn't controlling any pages
You refresh the page and at this point the page is controlled and requests will go through the service worker (resulting in the caching on the second load).

The service worker will not cache anything until its been activated. It gets activated only on the second hit itself. To achieve caching on the first hit you have to guide service worker to skip waiting for activation. you can do this by
self.addEventListener('install', () => {
self.skipWaiting(); //tells service worker to skip installing and activate it
/*your code for pre-caching*/
});
once its been skipped it enter the activated mode and will wait for caching but it wont cache the clients interaction. To do so apply the following line
self.addEventListener('activate', () => {
clients.claim();
});
which starts caching on the first hit itself

Related

Background sync replaying without background sync event

I am new to the service workers and trying to develop one to take care of background image uploading. I am using Workbox and firefox for testing. The service worker is loaded and registered correctly and whenever I try to upload an image offline these logs appear in the console:
workbox Request for '/photoUpload' has been added to background sync queue 'PhotoQueue'
workbox Using NetworkOnly to respond to '/photoUpload'
after some seconds before I get online, the following are printed in the log, and the photo is not uploaded to the server:
workbox Background sync replaying without background sync event
workbox Request for '/photoUpload' has been replayed in queue 'PhotoQueue'
workbox All requests in queue 'PhotoQueue' have successfully replayed; the queue is now empty!
here is my serviceWorker.js:
const showNotification = () => {
self.registration.showNotification('Post Sent', {
body: 'You are back online and your post was successfully sent!',
});
};
const bgSyncPlugin = new workbox.backgroundSync.Plugin('PhotoQueue', {
maxRetentionTime: 24 * 60, // Retry for max of 24 Hours
callbacks: {
queueDidReplay: showNotification
}
});
workbox.routing.registerRoute(
new RegExp('/photoUpload'),
new workbox.strategies.NetworkOnly({
plugins: [
bgSyncPlugin
]
}),
'POST'
);
is there a way that I can trigger the background sync event? why the workbox removing the POST request from the Queue before the image is uploaded to the server.
Firefox does not support the Background Sync API natively. workbox-background-sync will attempt to "polyfill" this missing API by automatically retrying the queue whenever the service worker starts up.
Chrome allows you to trigger the background sync event via its DevTools, but as mentioned, Firefox does not. There is no programmatic way to force a service worker to stop and then start again using DevTools in Firefox (as far as I know).
Are you sure that the photo isn't being uploaded to the server? Do you see anything in the Network panel of Firefox's DevTools corresponding to the upload attempt?

OneSignal registration fails after refresh

I am using OneSignal in my Laravel/Vue app. I have included it within <head> as stated in documentation:
<script src="https://cdn.onesignal.com/sdks/OneSignalSDK.js" async=""></script>
<script>
var OneSignal = window.OneSignal || [];
OneSignal.push(function() {
OneSignal.init({
appId: "{{ env('ONESIGNAL_APP_ID') }}"
});
OneSignal.showNativePrompt();
});
</script>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/OneSignalSDKWorker.js')
.then(function () {
console.log('Service worker registered');
})
.catch(function (error) {
console.log('Service worker registration failed:', error);
});
} else {
console.log('Service workers are not supported.');
}
</script>
I also have a service worker of my own, so I've followed the documentation here as well.
What is happening after a hard reset is that service worker gets installed and it is all fine, however once I refresh the page I am getting:
OneSignalPageSDKES6.js?v=151102:1 Uncaught (in promise) InvalidStateError: The current environment does not support this operation.
at Function.getServiceWorkerHref (https://cdn.onesignal.com/sdks/OneSignalPageSDKES6.js?v=151102:1:41510)
at xe. (https://cdn.onesignal.com/sdks/OneSignalPageSDKES6.js?v=151102:1:144028)
at Generator.next ()
at r (https://cdn.onesignal.com/sdks/OneSignalPageSDKES6.js?v=151102:1:716)
And I have no idea what does that mean? What is "current environment"? Where to start debugging? I've tried putting console logs around it, however it led me nowhere...
You would start debugging by looking at the source code of the library.
In your case your library is the OneSignal SDK for browsers.
Let's do this!!!
We can see that this error is thrown by getServiceWorkerHref function (which is defined here) and the error message is driven by the InvalidStateReason enumeration:
case InvalidStateReason.UnsupportedEnvironment:
super(`The current environment does not support this operation.`);
break;
If you look at the first linked file, you will see the note on getServiceWorkerHref OneSignal developers left for the those who dare venture into their source code:
else if (workerState === ServiceWorkerActiveState.Bypassed) {
/*
if the page is hard refreshed bypassing the cache, no service worker
will control the page.
It doesn't matter if we try to reinstall an existing worker; still no
service worker will control the page after installation.
*/
throw new InvalidStateError(InvalidStateReason.UnsupportedEnvironment);
}
As you can see, the error is raised when the service worker has the "Bypassed" state. What is that, you may ask? Let's look at ServiceWorkerActiveState enumeration below, in the same file:
/**
* A service worker is active but not controlling the page. This can occur if
* the page is hard-refreshed bypassing the cache, which also bypasses service
* workers.
*/
Bypassed = 'Bypassed',
It seems, when the browser "hard-refreshes" the page, it bypasses the service worker and OneSignal can't properly initialize when that happens. Hard-refresh can happen for a number of reasons — here are some of them (to the best of my knowledge):
if you click the refresh button a bunch of times (usually seconds consecutive refresh within a short period of time may trigger this)
if you have caching disabled in your DevTools
if the server sets a no-cache header
What is happening after a hard reset
I don't know exactly what you mean by "hard reset", but that sounds like it would trigger this issue. I would suggest you close your browser and then visit the page you are working on without using "reset" functions — theoretically, the service worker should be used for caching on consecutive visits and that would ensure OneSignal can function.

How to force loading the current version of an offline-able web app?

I'm doing a tiny offline-able web app / PWA. It's meant to be opened from a home screen icon and mimic a regular app by loading entirely from a cache when offline.
The app is written using Vue and to accomplish the above I'm just using their PWA template and whatever it generates. To the best of my knowledge what this does is set up workbox using the GenerateSW plugin to precache everything in the Webpack build, and registers it using register-service-worker. That is, I have fairly little control out of the box over the fine details, it's meant to be a turnkey solution.
That said, I'm not sure how to actually load a new build of the application when it's available. The above can detect this - the generated SW registration file with my changes looks like this:
import debug from 'debug';
import { register } from 'register-service-worker';
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready(...args) {
log('App is being served from cache by a service worker.\n', ...args);
},
cached(...args) {
log('Content has been cached for offline use.', ...args);
},
updated(...args) {
log('New content is available; please refresh.', ...args);
},
offline(...args) {
log('No internet connection found. App is running in offline mode.', ...args);
},
error(error, ...args) {
log('Error during service worker registration:', error, ...args);
}
});
}
When I make a new build of the application, and I refresh the app in a browser, the updated() callback is executed, but nothing else is done. When I tried adding:
window.location.reload(true);
which should be a forced refresh, I just get a refresh loop. I'm assuming this is because the service worker cache is completely independent from the browser cache and unaffected by things like the above or Ctrl+F5. (Which makes the "please refresh" rather misleading.)
Since this is going to mimic a native app, and it's supposed to be a simple line-of-business tool, I don't really need to do anything more complicated than immediately reload to the new version of the app when an update is available. How can I achieve this?
Okay so the behaviour I've observed is that the update does happen automatically, it's just not obvious as to what the exact sequence of events is. I'll try to describe my best understanding of how the generated service worker works in the installed PWA scenario. I'll speak in terms of "app versions" for simplicity, because the mental model behind this is closer to how apps, not webpages work:
You deploy v1 of your application to a server, install / precache it on a device, and run it for the first time.
When you suspend and resume your app, it does not hit your servers at all.
The app will check for an update when it's either cold-started, or you reload the page, i.e. using the pull down gesture on Android.
(Possibly also periodically as the cached version goes stale, but I haven't checked this.)
Say you've deployed v2 of your app in the meantime. Reloading an instance of v1 of the app will find this update, and precache it.
(One reason why prompting the user to reload doesn't seem to make sense. Whatever the reloading is meant to accomplish has already happened.)
Reloading an instance of v1 again does absolutely nothing. The app remains running between reloads, and you'll just get v1 afterwards.
(Reason number two why prompting the user to reload is pointless - it's not what causes a new version of an app to load.)
However, next time you cold start your app - e.g. nuke it from the task switcher and reopen - v2 of your app will be loaded and I'm guessing v1 will get cleaned out. That is, your app must fully shut down so an update will load.
In short, for an application to be updated from v1 to v2, the following steps need to occur:
Deploy v2 to server
Refresh instance of v1 on device, or shut down and reopen the app.
Shut down and reopen the app (again).

How can I list my blog posts already stored in the browser cache by a Service Worker?

My blog already has a working Service Worker that caches recent posts, and let users read even when they are offline.
On the offline page that is shown for content not available in the cache, I would like to list the recent posts that ARE in the cache, to give the user an opportunity to read them while offline.
Is there an easy way to list such content when in a standard window context, instead of a Service Worker one?
I can't find any tutorial for this. All the tutorials I find only deal with the Service Worker part.
Thanks.
In addition to being available in Workers, the Cache Storage API is also available as part of the window global scope, as window.caches.
Here's an exerpt from a full example of using that interface to get a list of all cache contents:
window.caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
window.caches.open(cacheName).then(function(cache) {
return cache.keys();
}).then(function(requests) {
requests.forEach(function(request) {
// Do something with request, like update your UI
// based on request.url.
});
});
});
});

concert ways to improve loading time in Android WebView

I am trying all means to improve the loading time of an link in android webview in vain.I am using a link which has images and text fetched from external server by using some js functions and those images and text may change on any moment.In this scenario the code I used is as below.I am not interested in HTML 5 caching or server caching techniques,as the same link loads faster on browser fails to do the same in android webview.There is not much js script i can load from resource to improve performance hence most of the script just pulls data from external server and images from on amazon server.
WebSettings settings = getSettings();
settings.setJavaScriptEnabled(true);
settings.setPluginState(PluginState.ON);
settings.setRenderPriority(WebSettings.RenderPriority.HIGH);
settings.setDomStorageEnabled(false);
settings.setLoadsImagesAutomatically(true);
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
settings.setJavaScriptEnabled(true);
settings.setAppCacheEnabled(false);
settings.setGeolocationEnabled(false);
1 .I guess turning on DomStaorage and AppCache to true have impact on loading time so i have turned it off. -> Is this true
2.In OnDestroy i call clearcache on webview to clear application cache - I am doing this hence i am afraid that my app size may grow as webview db grows if i fail to clear.-> is this must,or android handles this gracefully.
3.Few suggestion i hear is to set setCacheMode to LOAD_NO_CACHE and comment clearing webview cache in onDestroy. --> Does calling webview.clearcache() have any impact hence I have already set not to load from cache.
4.The link does not provide me information of previously refreshed time,in that case what cache mode could any one recommend.
5.Is there is any concert testing method to find out loading time on devices that takes network connection and server lags into account.hence the same page once it loads fast may load slow in some other time.
Give a try with "fast click".
mobile browsers will wait approximately 300ms from the time that you tap the button to fire the click event. The reason for this is that the browser is waiting to see if you are actually performing a double tap.
Download the script, add
<script type='application/javascript' src='fastclick.js'></script>
in your header and this code in your script.
window.addEventListener('load', function() {
FastClick.attach(document.body);
}, false);
More informations: https://developers.google.com/mobile/articles/fast_buttons

Resources