Samsung Internet Browser - 'beforeinstallprompt' event never fired when window.history.replaceState() is used

980 views Asked by At

Below is my code to add a button to let user to "install" my web site to a home screen on mobile. Everything is working well on every device with Chrome, but not with Samsung Internet Browser where 'beforeinstallprompt' is not fired at all (v.15.0.4.9).

let deferredPrompt;
const addPwaButton = document.querySelector('.add-pwa-button');

console.log("workerLoad.js");

window.addEventListener('beforeinstallprompt', function(e) {
  console.log("beforeinstallprompt");
  // 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 to notify the user they can add to home screen

  addPwaButton.addEventListener('click', function(e) {
    // Show the prompt
    deferredPrompt.prompt();
    // Wait for the user to respond to the prompt
    deferredPrompt.userChoice.then(function(choiceResult){
        if (choiceResult.outcome === 'accepted') {
            console.log('User accepted the A2HS prompt');
        } else {
          console.log('User dismissed the A2HS prompt');
        }
        deferredPrompt = null;
      });
  });
});

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

I put another listener in header of my page, to be sure that I do not miss the event, but not luck, the event is not fired:

window.addEventListener('beforeinstallprompt', function(e) {
  console.log("beforeinstallprompt!");
})

When event is fired. In my page I have a contact link like this: <a href="mailto:[email protected]">Contact</a>. And if I click the link then the operating system offers me a number of programs with which to open the link. BUT at the same time, the 'beforeinstallprompt' event is fired, so the install button appears. !!!

Before pressing link, this is the output in console:

workerLoad.js
ServiceWorker registration successful with scope:  https://example.com/

After pressing the link:

beforeinstallprompt
Banner not shown: beforeinstallpromptevent.preventDefault() called. The page must call beforeinstallpromptevent.prompt() to show the banner.

Remember, everything is working well on every device with Chrome. What can I do with Samsung Internet Browser to work?

UPDATE - I strip down the code till I manage to isolate the problem with Samsung Internet Browser:

<!doctype html> 
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <link rel="manifest" href="/manifest.json">
      <script>
         if ('serviceWorker' in navigator) {
           window.addEventListener('load', function() {
             navigator.serviceWorker.register('/sw.js').then(function(registration) {
               // Registration was successful
               console.log('ServiceWorker registration successful with scope: ', registration.scope);
             }, function(err) {
               // registration failed :(
               console.log('ServiceWorker registration failed: ', err);
             });
           });
         }

         let deferredPrompt;
         window.addEventListener('beforeinstallprompt', function(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 to notify the user they can add to home screen
           console.log("beforeinstallprompt!");
         });

         // this is the line that create the problem, no matter what are the parameters
         window.history.replaceState( {}, "", "" );
         
      </script>
   </head>
   <body>
      test
   </body>
</html>

If I remove this line window.history.replaceState( {}, "", "" ), event 'beforeinstallprompt' is fired and the output in console is:

ServiceWorker registration successful with scope:  https://example.com/
beforeinstallprompt!
Banner not shown: beforeinstallpromptevent.preventDefault() called. The page must call beforeinstallpromptevent.prompt() to show the banner.

If I do not remove the line window.history.replaceState( {}, "", "" ), event 'beforeinstallprompt' is never fired and the output in console is:

ServiceWorker registration successful with scope:  https://example.com/
1

There are 1 answers

1
Rafał Wolak On

did you check this solution? https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent/prompt

let deferredPrompt;
let isTooSoon = true; // <-- add this

const addPwaButton = document.querySelector('.add-pwa-button');

console.log("workerLoad.js");

window.addEventListener('beforeinstallprompt', function(e) {
  if (isTooSoon) {  // <-- add this
    console.log("beforeinstallprompt");
    // 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 to notify the user they can add to home screen

    addPwaButton.addEventListener('click', function(e) {
      isTooSoon = false;  // <-- add this

      // Show the prompt
      deferredPrompt.prompt();
      // Wait for the user to respond to the prompt
      deferredPrompt.userChoice.then(function(choiceResult){
          if (choiceResult.outcome === 'accepted') {
              console.log('User accepted the A2HS prompt');
          } else {
            console.log('User dismissed the A2HS prompt');
          }
          deferredPrompt = null;
        });
    });
  }
});

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}