March 22, 2019

Create an Ionic 4 PWA with Capacitor

by Ahmed Bouchefra

Create an Ionic 4 PWA with Capacitor

In this tutorial, we’ll learn to build a Progressive Web Application (PWA) with Ionic 4 and Capacitor.

A PWA is a web application similar to traditional web apps but provides extra features for users that were only available in native mobile apps like instant loading, add-to-home screen, splash screens, access to device capabilities, push notifications and offline support.

The term “Progressive” in Progressive Web Apps refers to how these apps provide web experiences which are reliable - by loading instantly regardless of network conditions - fast - by responding immediately and smoothly to every user interaction - and engaging - by providing an immersive and natural user experience.

To achieve these demanding user experience primers, a PWA makes use of Service Workers for supporting features like instant loading and offline support so it needs to be securely served via HTTPS, as Service Workers only work under HTTPS connections. PWAs achieve support for all modern and old browsers, which is an important aspect of this kind of apps. Since they use modern features that might not be available in all browsers, PWAs make use of progressive enhancement which is using the feature when it’s available and use a fallback for browsers which don’t support it.

The application we'll be building is a simple JavaScript Jargon app that's based on the Simplified JavaScript Jargon available on GitHub. We'll export the entries as JSON data and we'll consume them from our PWA. We already created a statically generated JSON API available here.

You can find the source code of this app in this GitHub repository.

Note: If you would like to consume an API from your server, make sure you have CORS enabled in your web server. Otherwise, the web browser will block your requests due to the Same Origin Policy available on modern browsers.

Now, let’s get started!

Prerequisites

  • You will need to have a development environment with Node.js and npm installed. You can install both of them by going to the official website and grab the binaries for your system.
  • Familiarity with TypeScript since we’ll be using Ionic 4 with Angular.

Generating a New Ionic 4 Project

Let’s start by installing Ionic CLI globally on your system from npm using the following command:

npm install -g ionic

Using the CLI, you can generate a new project using the following command:

ionic start

The CLI will prompt you for information about your project, such as the name (enter jsjargonpwa) and starter template (choose sidemenu). This will set up your project.

When prompted for Install the free Ionic Appflow SDK and connect your app? (Y/n) just type n.

You can now navigate to your project’s folder and serve your application locally using:

cd ./jsjargonpwa
ionic serve

Your application will be available from the http://localhost:8100 address.

We’ll be working on the home page, so you can remove the list page. First, delete the list folder containing the files; next, open the src/app/app.component.ts file and delete the entry for the list page from the appPages array:

     public appPages = [
       {
         title: 'Home',
         url: '/home',
         icon: 'home'
       }
     ];

Next, open the src/app/app-routing.module.ts file and delete the route for the list page:

   const routes: Routes = [
     {
       path: '',
       redirectTo: 'home',
       pathMatch: 'full'
     },
     {
       path: 'home',
       loadChildren: './home/home.module#HomePageModule'
     }
   ];

Getting JSON Data

We’ll use HttpClient from Angular to send a GET request to our server to fetch the JSON entries. Before that, we need to import it in our project.

First, open the src/app/home/home.module.ts file and import HttpClientModule:

   // [...]
   import { HttpClientModule } from '@angular/common/http';
  
   @NgModule({
     imports: [
       /* [...] */
       HttpClientModule
     ],
     declarations: [HomePage]
   })
   export class HomePageModule {}

Next, open the src/app/home/home.page.ts file and update it accordingly:

   import { Component } from '@angular/core';
   import { HttpClient } from '@angular/common/http';
   @Component({
     selector: 'app-home',
     templateUrl: 'home.page.html',
     styleUrls: ['home.page.scss'],
   })
   export class HomePage {
     API_URL = "https://media.jscrambler.com/blog/data.json";
     entries: Array<any>;
     constructor(private httpClient: HttpClient){
     }
     ionViewDidEnter(){
       this.getData();
     }
     getData(){
       this.httpClient.get(this.API_URL).subscribe((entries: any[])=>{
         this.entries = entries;
       })
     }
  
   }

We declare two variables: API_URL — which holds the address of the JSON file that we need to fetch — and entries, an array that will hold the entries.

Now, let’s inject HttpClient as httpClient via the component’s constructor.

Next, we add a getData() method that calls the get() method of HttpClient and subscribe to the returned Observable. We then assign the fetched data to the entries variable.

Finally, we add the ionViewDidEnter() event that gets called when the Ionic page is loaded and we call the getData() method to fetch the entries once the page is loaded.

Next, open the src/app/home/home.page.html file and update it as follows:

   <ion-header>
     <ion-toolbar>
       <ion-buttons slot="start">
         <ion-menu-button></ion-menu-button>
       </ion-buttons>
       <ion-title>
         JSJargon
       </ion-title>
     </ion-toolbar>
   </ion-header>
   <ion-content>
       <ion-list lines="none">
         <ion-item *ngFor = "let entry of entries">
           <ion-card>
             <ion-card-header>
               <ion-card-title>{{ entry.name }}</ion-card-title>
             </ion-card-header>
             <ion-card-content>
               <p>{{ entry.description }}</p>
             </ion-card-content>
           </ion-card>
         </ion-item>
       </ion-list>
   </ion-content>

We simply loop through the entries variable and display the name and description of each entry using an Ionic card.

This is a screenshot of the result:

App Screenshot 1

Adding Capacitor

Capacitor is an open source native container (similar to Cordova) built by the Ionic team that you can use to build web/mobile apps that run on iOS, Android, Electron (Desktop), and as Progressive Web Apps with the same code base. It allows you to access the full native SDK on each platform, and easily deploy to App Stores or create a PWA version of your application.

Capacitor can be used with Ionic or any preferred frontend framework and can be extended with plugins. It has a rich set of official plugins and you can also use it with Cordova plugins.

Protect your Code with Jscrambler

Installing Capacitor

Let’s start by installing Capacitor in your project:

npm install --save @capacitor/cli @capacitor/core

Next, you need to initialize Capacitor with npx cap init \[appName\] [appId]:

npx cap init jsjargon com.techiediaries.jsjargon

Using the Clipboard Plugin

Now, let’s use the Clipboard Capacitor plugin in our project to see how Capacitor works by example.

Open the src/app/home/home.page.ts file and add:

import { Plugins } from '@capacitor/core';
const { Clipboard } = Plugins;

Next, add the copy() method which will be used to copy a JS term to the clipboard:

     async copy(name: string, text: string){
       Clipboard.write({
         string:  name + ' is ' + text
       });
     }

Finally, open the src/app/home/home.page.html file and add a button to the Ionic card for each entry:

           <ion-card>
             <ion-card-header>
               <ion-card-title>{{ entry.name }}</ion-card-title>
             </ion-card-header>
             <ion-card-content>
               <p>{{ entry.description }}</p>
             </ion-card-content>
               <ion-button (click)="copy(entry.name, entry.description)">
                 Copy
               </ion-button>
           </ion-card>

This is a screenshot of the result:

App Screenshot 2

Adding a Web Manifest and A Service Worker

A web manifest and service worker are essential requirements for a PWA. You can add both of them using the @angular/pwa package. In your terminal, run:

cd jsjargonpwa
ng add @angular/pwa

This is a screenshot of what @angular/pwa has added and updated in your project:

CLI Angular Added

For example, a src/manifest.webmanifest file is created and referenced in the index.html file:

   <link rel="manifest" href="manifest.webmanifest">

Also, different default icons were added in the src/assets/icons folder. In production, you will need to change these icons with your own.

In the src/app/app.module.ts file, a service worker is registered using the following line:

   ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })

This is only enabled for production so you will need to build your application for production to register the service worker.

Next, we need to build our application for production using the following command:

ionic build --prod

Finally, we need to use a hosting service like Netlify to securely host the application over HTTPS (required by a PWA). Here is the link to our hosted PWA.

Conclusion

In this tutorial, we’ve seen how to create a PWA with Ionic 4, Angular and Capacitor.

We have seen an example of sending GET requests using HttpClient and how to access the device clipboard using the Clipboard Capacitor plugin. We hope that this tutorial has been useful in helping you with your own projects.

If you're building Ionic applications with sensitive logic, be sure to protect them against code theft and reverse-engineering by following our guide.