Is there a way to know if a progressive web app is in the background or in the foreground (ie. it is currently the app in focus or is it "minimized")
Searching Google and SO for the past 2 days brought only information on how to run a service worker in the background. Thanks!
Using a service worker, a Progressive Web App (PWA) can do work in the background, even when the user isn't using the app. Service workers used to be reserved for native apps, but they are now also available to PWAs, providing a better offline experience.
Right click on the website you want to check, click Inspect Element. Then, go to Application tab > Service Workers. You can easily see if there are any Service Workers on that site. Again, this trick only gives you a hint of the possibility a certain website is a PWA.
Although a progressive web app uses similar technologies as used by web apps like HTML, CSS, JavaScript, etc. It provides the user experience of a native app. Further, unlike web apps, a PWA can use the majority of device features like push notifications regardless of the web browser used.
Foreground refers to the active apps which consume data and are currently running on the mobile. Background refers to the data used when the app is doing some activity in the background, which is not active right now.
From a client you can use the Page Visibility API to query the current visibility and to get events when the visibility changes.
const state = document.visibilityState;
document.addEventListener('visibilitychange', () => {
console.log(document.visibilityState);
});
From Service Workers you can also query the visibility state of clients with WindowClient.
event.waitUntil(
clients.matchAll({ type: 'window' })
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
const state = clientList[i].visibilityState;
}
})
);
I've searched all over for this for days, lots of the same answer as per above. Whilst the visibility api does work to an extent, it doesn't work all the time. Thought I'd post our solution here to supplement the answer above with what we have found to be the most reliable way that works across most browsers.
The issue for us was on mobile devices more than anything else. We have a PWA that is installable, but the problem exists in the mobile browser as well. Once the installed PWA (or tab open in mobile browser) is pushed into the background, the visibility API isn't sufficient on it's own. Our app relies on realtime data updates via graphql subscriptions, when the app/browser goes into the background, it no longer gets this data. If user was to then open the app after getting a push notification indicating they have a new message for example, the new message isn't in the app as it's effectively been asleep and it's websockets closed. We don't want to have to do a full refresh everytime the user moves between our app and other apps on their device, so using the low level check in the service worker and just reloading the page, wasn't going to provide a great experience and cause a lot of unnecessary loading. We needed to reliably know inside our react components, when the app had come back into the foreground, so that we could force a fetch of relevant missing data from when it went into the background.
We ended up adapting this from the google documentation on page lifecycle. Someone did create a small js library for it, but we couldn't get that to work. So we just adapted the example code and it's been working pretty well.
https://developers.google.com/web/updates/2018/07/page-lifecycle-api
The below code can easily be integrated into a react component and managed through useState and useEffect hooks.
import MobileDetect from 'mobile-detect';
const md = new MobileDetect(window.navigator.userAgent);
const isMobile = md.mobile();
let standalone
if (navigator.standalone) {
standalone = 'standalone-ios';
}
if (window.matchMedia('(display-mode: standalone)').matches) {
standalone = 'standalone';
}
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
let displayState = getState();
const onDisplayStateChange = () => {
const nextState = getState();
const prevState = displayState;
if (nextState !== prevState) {
console.log(`State changed: ${prevState} >>> ${nextState}`);
displayState = nextState;
//standalone will restrict to only running for an installed PWA on mobile
if (nextState === 'active' && isMobile /* && standalone */) {
//The app/browser tab has just been made active and is visible to the user
//do whatever you want in here to update dynamic content, call api etc
}
}
};
//subscribe to all of the events related to visibility
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
window.addEventListener(type, onDisplayStateChange, {capture: true});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With