Hacky way to detect if app was launched in standalone mode in Chromium (with manifest.json) without problems with AppCache
(and possibly ServiceWorker
).
As suggested on one of the Google Developers sites, one may use search params in start_url
of the manifest.json
to notify page about that it was launched in standalone mode: start_url: '/?standalone'
.
This works well unless your page uses AppCache
(or ServiceWorker
). In case of AppCache
, every url with different search params is treated as separate entry in cache. So if you have listed /
path in AppCache
and naively used start_url: '/?standalone'
in your manifest.json
, then your Web App won't work offline from the Home Screen.
With ServiceWorker
, however, there is few ways to fix this directly in its code, like ignoreSearch
option for match()
method (currently not supported in Chromium) or just traversing cache entries manually and comparing new URL(event.request.url).pathname
with stored request new URL(...).pathname
.
// manifest.json
{
...,
"start_url": "/#:standalone:",
...
}
function detectStandalone() {
const hash = window.location.hash;
let standalone = false;
if (hash === '#:standalone:') {
// first run (open app) in standalone mode
// cache state in sessionStorage
standalone = true;
sessionStorage.setItem(':standalone:', '1');
// remove hash part from the url before actual app start,
// in case if your app uses hash (#) routing
history.replaceState(history.state, '', '/');
} else if (sessionStorage.getItem(':standalone:')) {
// second and subsequent runs (reloads)
// sessionStorage is unique per tab and Home Screen app is just a
// chrome-less tab. So it's safe to assume
// that user is still in standalone mode
standalone = true;
} else {
// neither first, nor subsequent standalone runs, normal mode
// do nothing
}
return standalone;
}
Chromium has display-mode
media query in trunk now and it's already available in Chrome Canary.
It could be used like this:
@media (display-mode: standalone) {
/* do something */
}
So once feature will became stable (matchMedia('(display-mode)').matches
does not works correctly yet), you could (and should) use it from JS to detect standalone mode of the app. And it's safe to include code of display-mode
check into your code right now.
Here is full code with display-mode
media query and previous hacky way:
function isWebAppStandalone() {
const STANDALONE = ':standalone:';
const hash = window.location.hash;
let standalone = false;
if (hash === '#' + STANDALONE) {
standalone = true;
history.replaceState(history.state, '', '/');
}
if (matchMedia('(display-mode)').matches) {
return matchMedia('(display-mode: standalone)').matches;
}
if (standalone) {
sessionStorage.setItem(STANDALONE, '1');
} else if (sessionStorage.getItem(STANDALONE)) {
standalone = true;
}
return standalone;
}