check if user has already installed PWA to homescreen on Chrome?
I'm trying to create an "Add To Home Screen" button on my progressive web app, as described in Chrome's documentation.
I'm generally following the prescribed pattern, where I have some hidden button which is displayed when Chrome's beforeinstallprompt
event fires.
I capture the event once it fires, and then use the event to begin the native install dialogue once my own install button is clicked. The sample code is below:
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
});
btnAdd.addEventListener('click', (e) => {
// hide our user interface that shows our A2HS button
btnAdd.style.display = 'none';
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
} else {
console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
});
The issue I'm running into is that I don't want to show my install button (btnAdd
) if the user has already installed the web app to thier home screen, and I'm having trouble figuring out how to check for that scenario.
I was hoping to modify the above code as follows:
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// If the user has not already installed...
deferredPrompt.userChoice
.then(choiceResult => {
if (choiceResult === undefined) {
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
}
});
});
So that the install button won't be displayed if the user has already installed. But this doesn't seem to work. It appears that if they haven't made a choice already, accessing userChoice
just prompts the user directly with the native dialogue.
I'm not really sure how the beforeinstallevent
works, so this might not even be a good strategy. Ideally I was hoping this would work something like something like navigator.serviceWorker.ready()
, which returns a Promise rather than using browser events to try and figure out when stuff is ready.
In any case, are there any ideas on how I can check that the user has installed to home screen before I show my own home screen install button?
Edit: As Mathias has commented, checking for the event before showing the button should be sufficient. I believe the issue I was having is a result of using localhost, which appears to continually fire the beforeinstallprompt
event even after installation, which is not the intended behavior. Hosting the code solved the issue.
Solution 1:
Perhaps, don't show the button until you intercept the automatic pop-up?
or
In your code, check to see if the window is standalone
If it is, you need not show the button
if (window.matchMedia('(display-mode: standalone)').matches) {
// do things here
// set a variable to be used when calling something
// e.g. call Google Analytics to track standalone use
}
My example tester here
https://a2hs.glitch.me
Source code for my tester
https://github.com/ng-chicago/AddToHomeScreen
Solution 2:
To answer original question. With latest versions of Chrome you can use window.navigator.getInstalledRelatedApps()
. It returns a promise with an array of installed apps that your web app specifies as related in the manifest.json. To enable this to work you need to add related_applications
field to manifest.json
"related_applications": [{
"platform": "webapp",
"url": "https://app.example.com/manifest.json"
}]
And then you can use it like:
//check if browser version supports the api
if ('getInstalledRelatedApps' in window.navigator) {
const relatedApps = await navigator.getInstalledRelatedApps();
relatedApps.forEach((app) => {
//if your PWA exists in the array it is installed
console.log(app.platform, app.url);
});
}
Source: API docs
Now you can display some elements depending if your app is installed. E.g: you can display "Open app" button and redirect user to PWA. But remember to disable it when the user is already in the app using @Mathias's answer and checking (display-mode: standalone)
However, regarding your use case. You should display install button only when beforeinstallprompt
is intercepted. Browser does not fire this event if the PWA is already installed on the device. And when prompt is fired and choiceResult.outcome === 'accepted'
you hide the button again.