google-chrome-devtoolsservice-worker-eventsbeforeinstallprompt

Service Worker -> addEventListener -> beforeinstallprompt will not run A2HS on my computer for a given website in chrome


I'm having the worst week trying to figure out how to reset my Chrome browser settings because I have a fully functioning website that contains a working manifest, service worker and A2HS script. It works perfectly, except on any device that I personally own with Google Chrome on it...

Meaning, I have a desktop and an Android cell phone with the latest version of Google Chrome on both (as of April 9th, 2020 v81.0.4044.92 (Official Build) (64-bit)) and I can not get the addEventListener -> beforeinstallprompt to fire on either. I've double-checked the 'Apps' button, I've double-checked my desktop in both cases and I've cleared out the storage under developer tools -> Application. But no mater, nothing allows me to call addEventListener -> beforeinstallprompt on these two devices.

Here's the real kicker, it works perfectly on everyone else's computer I try! It's only refusing to work on my development systems. It has to be something to do with the security settings on my own devices but I need to figure this out because it could happen to a visitor of this site or my client's systems as well and I'm totally out of ideas, I've been working on this for 3 days. There's got to be a magic button that will fix this in the Chrome settings somewhere.

Any thoughts?

Much appreciated.

Vince

UPDATE

Ok, I've tested this on a system that didn't even have Chrome installed on it before and it doesn't want to work there either so cache cant be the issue anymore, its got to be something wrong with my code. Here's what I got.

Manifest:

{
    "short_name": "ASDF",
    "name": "ASDF Dentistry",
    "description": "At ASDF Dentistry bla bla bla.",
    "icons": [
        {
            "src": "/ccmstpl/_img/ico/android-chrome-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/ccmstpl/_img/ico/android-chrome-256x256.png",
            "sizes": "256x256",
            "type": "image/png"
        },
        {
            "src": "/ccmstpl/_img/ico/android-chrome-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "start_url": "/",
    "theme_color": "#006058",
    "background_color": "#006058",
    "display": "standalone",
    "scope": "/",
    "lang": "en",
    "dir": "ltr"
}

ServiceWorker:

if(navigator.serviceWorker){
    window.addEventListener('load',() => {
        navigator.serviceWorker
        .register('/sw.js')
        .then(console.log('[ServiceWorker] Registered Successfully'))
        .catch(err => console.log(`[ServiceWorker] Error: ${err}`));
    });
} else {
    console.log('Service Worker not supported.');
}

Install Button:

<div id="installContainer" class="hidden">
    <button id="butInstall" type="button">
        Install
    </button>
</div>

CSS:

.hidden{display:none !important}

#installContainer{
    position:absolute;
    top:1em;
    display:flex;
    justify-content:center;
    width:100%;
    z-index:10;
}

#installContainer button{
    background-color:green;
    border:1px solid white;
    color:white;
    font-size:1em;
    padding:0.75em
}

#installContainer button:hover{background-color:lightGreen}

Add to Home Screen (A2HS) Code:

let deferredPrompt;
const divInstall = document.getElementById('installContainer');
const butInstall = document.getElementById('butInstall');

window.addEventListener('beforeinstallprompt',(e) => {
    console.log('beforeinstallprompt','beforeinstallprompt', e);
    // Prevent Chrome 76 and later from showing the mini-infobar
    e.preventDefault();
    // Stash the event so it can be triggered later.
    deferredPrompt = e;
    // Remove the 'hidden' class from the install button container
    divInstall.classList.toggle('hidden',false);

    butInstall.addEventListener('click',(e)=>{
        // hide our user interface that shows our A2HS button
        divInstall.classList.toggle('hidden',true);
        // 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;
        });
    });
});

In the A2HS code it never executes this line:

window.addEventListener('beforeinstallprompt',(e) => {

I think the reason this worked when I checked it on my wifes computer is because it was still running an older cached version from a couple weeks ago. Any ideas?


Solution

  • Got it.

    Turns out you need to keep the A2HS code close to the ServiceWorker register code.

    I had the code to register the ServicesWorker in my index page but I had the code to set up the A2HS code in my main javascript file which was called later on. Turns out this separation of code will generate no error message and will completely stall the

    window.addEventListener('beforeinstallprompt',(e) => {
    

    code. Which makes troubleshooting it extremely difficult. So the solution is, just stack them on top of each other like this:

    if(navigator.serviceWorker){
        window.addEventListener('load',() => {
            navigator.serviceWorker
            .register('/sw.js')
            .then(console.log('[ServiceWorker] Registered Successfully'))
            .catch(err => console.log(`[ServiceWorker] Error: ${err}`));
        });
    } else {
        console.log('Service Worker not supported.');
    }
    
    let deferredPrompt;
    const divInstall = document.getElementById('installContainer');
    const butInstall = document.getElementById('butInstall');
    
    window.addEventListener('beforeinstallprompt',(e) => {
        console.log('beforeinstallprompt','beforeinstallprompt', e);
        // Prevent Chrome 76 and later from showing the mini-infobar
        e.preventDefault();
        // Stash the event so it can be triggered later.
        deferredPrompt = e;
        // Remove the 'hidden' class from the install button container
        divInstall.classList.toggle('hidden',false);
    
        butInstall.addEventListener('click',(e)=>{
            // hide our user interface that shows our A2HS button
            divInstall.classList.toggle('hidden',true);
            // 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;
            });
        });
    });
    

    I hope this can help someone else.