Menu

Creating a Progressive Web App (PWA) service to include all features Angular

Progressive Web Apps is the future of web apps, it brings a lot of capabilities to make our life easier and making it a lot closer to a native mobile app. It is not a native app but it does give you the offline capabilities, installation, updates.

I have been doing PWA for a few months and loving it, I would like to build the service that includes all PWA features and use it on my components. HaveĀ  a look below to see the implementation, it includes

  • a check for network status (see if you are offline on online)
  • to check if the app is installed or not, you can use this to show a prompt to end-user, asking them to install it.
  • option to automatically check for updates, you can wan the user that there is a new version and you can refresh the browser to get new updates. this can be really helpful if the app is never closed and running on KIOSK mode.
import { ApplicationRef, Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import { Observable, Subject } from 'rxjs';
import { concat, interval } from 'rxjs';
import { first } from 'rxjs/operators';

export enum NetworkStatus {
  ONLINE = 'online',
  OFFLINE = 'offline'
}

export enum InstalledStatus {
  INSTALLED = 'appinstalled',
  BEFORE_INSTALL = 'beforeinstallprompt'
}

@Injectable({
  providedIn: 'root'
})
// @ts-ignore
export class PwaService {
  offline: boolean;
  installEvent: any;

  public installed: Subject<any> = new Subject();

  constructor(private swUpdate: SwUpdate, private appRef: ApplicationRef) {
    this.subscribeNetworkStatus();
    this.subscribeInstalledStatus();
    this.subscribeApplicationUpdates();
    this.registerAutoUpdateCheck();
  }

  subscribeNetworkStatus() {
    window.addEventListener(NetworkStatus.ONLINE, this.onNetworkStatusChange.bind(this));
    window.addEventListener(NetworkStatus.OFFLINE, this.onNetworkStatusChange.bind(this));
  }

  subscribeInstalledStatus() {
    window.addEventListener(InstalledStatus.BEFORE_INSTALL, event => {
      this.installEvent = event;
      this.installed.next(event);
    });
  }

  subscribeApplicationUpdates() {
    this.swUpdate.available.subscribe(event => {
      const r = confirm('New version available, do you wish to update ?');
      if (r) {
        window.location.reload();
      }
    });
  }

  registerAutoUpdateCheck() {
    const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true));
    const everySixHours$ = interval(6 * 60 * 60 * 1000);
    const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$);
    everySixHoursOnceAppIsStable$.subscribe(() => this.swUpdate.checkForUpdate());
  }

  onNetworkStatusChange() {
    this.offline = !navigator.onLine;
  }

  install() {
    this.installEvent.prompt();
  }
}

in your component, you can use it like this

import { Component } from '@angular/core';
import { PwaService } from '@services/pwa.service';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
// @ts-ignore
export class AppComponent implements OnInit, OnDestroy {
  constructor(pwa: PwaService) { 
// if you import your service as a public then you can use it like below, else you will have to set a variable
  }
}

you html can be like this

<main role="main" class="container-fluid">
 <div *ngIf="pwa.offline">offline</div>

  <button class="github-star-badge" *ngIf="pwa.installed | async" (click)="pwa.install()">
    Install
  </button>

  <router-outlet></router-outlet>
</main>

 

Leave a comment