Add to Home Screen your ionic PWA

Table of Content

add-to-home-screen

One of the feature of Progressive Web Apps is the ability to add to home screen your application, in a mobile phone or on a desktop computer, if using Chrome (recommended) it handles most of the heavy lifting for you, and on Android, Chrome will generate a WebAPK creating an even more integrated experience for your users.

Starting on Chrome 68 (beta in early June 2018), Chrome will not automatically show the Add to Home Screen banner, instead, you must show it by calling prompt() on the beforeinstallprompt event.

Criteria to fire the "beforeinstallprompt" event

- The web app is not already installed
- Meets a user engagement heuristic (currently, the user has interacted with the domain for at least 30 seconds)
- Includes a web app manifest that includes:
   short_name or name
   icons must include a 192px and a 512px sized icons
   start_url
   display must be one of: fullscreen, standalone, or minimal-ui
- Served over HTTPS (required for service workers)
- Has registered a service worker with a fetch event handler

Note: Other browsers may have different criteria to trigger the beforeinstallprompt event, check their respective sites for full details: Edge, Firefox, Opera.

Show the add to home screen prompt

In order to prompt to the users the Add to Home Screen prompt, you need to:

1- Listen for the beforeinstallprompt event
2- Notify the user your app can be installed with a button or other element that will generate a user gesture event.
3- Show the prompt by calling prompt() on the saved beforeinstallprompt event.

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 on the button event.
  deferredPrompt = e;
// Update UI by showing a button to notify the user they can add to home screen
  btn.style.display = 'block';
});

//button click event to show the promt
btn.addEventListener('click', (e) => {
  // hide our user interface that shows our button
  btn.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 prompt');
      } else {
        console.log('User dismissed the prompt');
      }
      deferredPrompt = null;
    });
});

You can only call deferredPrompt.prompt() on the deferred event once, if the user dismissed it, you'll need to wait until the beforeinstallprompt event is fired on the next page navigation.

How to determine if the app was installed

To determine if the application was successfully added to the user's home screen once they accepted the prompt, you need to listen for the appinstalled event.

window.addEventListener('appinstalled', (event) => {
 console.log('installed');
});

Detecting with JavaScript if you app is launched from the home screen

if (window.matchMedia('(display-mode: standalone)').matches) {
  console.log('display-mode is standalone');
}

Safari

if (window.navigator.standalone === true) {
  console.log('display-mode is standalone');
}

Easiest way to test if the beforeinstallprompt event will be fired

Easiest way is to use Lighthouse to audit your app, and check the results of the User Can Be Prompted To Install The Web App test.

Practical example with ionic

For this example I going to use ionic, we are going to generate a PWA with ionic, for this example I assume you are familiar with ionic and you have installed it on your computer, if not, then you can read the here

Lets generate our ionic app:

After you create the ionic blank app, lets run ionic serve:

cd pwa
ionic serve

You should see something like this on your browser:

Open your application using google chrome, open the dev tool, go to audit and run the Progressive web app audit:

You'll notice that the app is only 45% PWA out of the box, we should make some changes first to comply with the requirements to trigger the beforeinstallprompt event:

Registering the service worker

Open the ionic app on your preferred IDE (I'll use Microsoft code) and go to ./src/index.html and uncomment this line:

<!-- un-comment this code to enable service worker
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js')
        .then(() => console.log('service worker installed'))
        .catch(err => console.error('Error', err));
    }
  </script>-->

The another requirement is to have a manifest.json, fortunately ionic generates one for us:

{
  "name": "Ionic",
  "short_name": "Ionic",
  "start_url": "index.html",
  "display": "standalone",
  "icons": [{
    "src": "assets/imgs/logo.png",
    "sizes": "512x512",
    "type": "image/png"
  }],
  "background_color": "#4e8ef7",
  "theme_color": "#4e8ef7"
}

Now lets provide a fallback when JavaScript is not available by just adding a < noscript > tag on the index:

<noscript>
This application needs JavaScript to work, please enable JavaScript on your browser
</noscript>

With just that, we were able to go from 45 to 82:

Now we need to deploy our app to firebase hosting to be able to have HTTPS, and a 100 score on the lighthouse.

Install firebase tools

npm install -g firebase-tools

Initialize your site

$ firebase init

To deploy your site, run the following command from your project's root directory:

$ firebase deploy

For more info go to https://firebase.google.com/docs/hosting/deploying

Now you can sun the lighthouse tool and obtain a 100 score:

Capturing the event

Go back to the ionic app code, locate the pages folder on src/pages/home and edit the home.html and home.ts as follow:

home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  The world is your oyster.
  <p>
    If you get lost, the <a href="http://ionicframework.com/docs/v2">docs</a> will be your guide.
  </p>
  <button class="btn" ion-button full (click)="add_to_home(event)" *ngIf="showBtn" >Install</button>
</ion-content>

home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  showBtn: boolean = false;
  deferredPrompt;
  constructor(public navCtrl: NavController) {
    
  }

  ionViewWillEnter(){
    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 on the button event.
      this.deferredPrompt = e;
      
    // Update UI by showing a button to notify the user they can add to home screen
      this.showBtn = true;
    });
    
    //button click event to show the promt
            
    window.addEventListener('appinstalled', (event) => {
     alert('installed');
    });
    
    
    if (window.matchMedia('(display-mode: standalone)').matches) {
      alert('display-mode is standalone');
    }
  }

  add_to_home(e){
    debugger
    // hide our user interface that shows our button
    // Show the prompt
    this.deferredPrompt.prompt();
    // Wait for the user to respond to the prompt
    this.deferredPrompt.userChoice
      .then((choiceResult) => {
        if (choiceResult.outcome === 'accepted') {
          alert('User accepted the prompt');
        } else {
          alert('User dismissed the prompt');
        }
        this.deferredPrompt = null;
      });
  };

}

Test in your browser

To test the functionality, we could make some changes on chrome to force to trigger the event (taken from here https://stackoverflow.com/a/50626433/4320602)

For security reasons, as others have written as well, browsers don't allow you to manually trigger the install event.

However, there is a way you can test it yourself. Go to chrome://flags and enable "Bypass user engagement checks"

This will kick off the prompt so you can test.

now run your app and click on the install button, you should see something like this:

Now the app will run as standalone app:

Happy coding,
Cheers