Building your first Progressive Web App With 6 Simple Steps

Progressive web applications are the next generation web, they push the mobile web forward and gives native feel to the users reducing the installation overhead and giving high performance web apps.

PWAs are essentially fast, more reliable and performance-focused web apps that are streamlined for the mobile users. Traditional native apps delivers a rich experience but they come up with a high cost like storage constraints, lack of real time updates etc., but PWAs has overcome all these constraints and also gives you an native app like feel as it provides push notifications, adding the icon to home screen, offline functionality, and so on. There are many business platforms that had moved to PWAs from native apps notably Forbes has announced that moving to PWAs has given them 43% increase in users and 100% increase in session duration.

In this tutorial we will build a simple PWA application (HTML/ CSS/ JS) to give you an over all idea, from which you can wave your imagination power to build much complex applications.

STEP 1:

Start by creating a simple Index.html file to display the news fetched from the API.

</pre>
<header>
<h1>Breaking News</h1>
</header>
<pre>
<span id="newsCount">Showing 0 Results</span></pre>
<div class="container"></div>
<pre>


STEP 2:

Add a javascript file (app.js) to get the latest news using NEWS API, and appends it to the Index.html

var APIKey = "c1bc494c90d34d028a4c3f17101cdb4d";

//INIT Method
window.addEventListener('load', e =&gt; {
    GetLatestNews();
});

//Fetches the latest news from NEWS API and appends it to the UI
async function GetLatestNews()
{
     var response = await fetch(`https://newsapi.org/v2/top-headlines?sources=bbc-news&amp;apiKey=${APIKey}`);
     var jsonResponse = await response.json();
     var mainDOM = document.querySelector('.container');
     mainDOM.innerHTML = '';
     mainDOM.innerHTML = jsonResponse.articles.map(GetNewsTemplate).join('\n');
     var resultCountDOM = document.getElementById('newsCount');
     resultCountDOM.innerHTML = `Showing ${jsonResponse.totalResults} Results`;
}

function GetNewsTemplate(news)
{
    return `</pre>
<div class="panel panel-default">
<div class="panel-heading">
<h2>${news.title}.</h2>
</div>
<div class="panel-body">
<h4>${news.description}</h4>
<div><img src="${news.urlToImage}" /></div>
</div>
</div>
<pre>
`;
}

Now this is how our web app will look like,

news feed


STEP 3:

As of now we have designed a normal web app, to display the news. Now we will add a service worker so that we can make use of CACHE API and FETCH API to make it as a PWA. Before registering a service worker check if the browser’s navigator object has service worker, if so then register it. Below given is a example of a simple SW being registered.


//Check for the service worker in navigator object, the register the service worker
if("serviceWorker" in navigator)
{
     window.addEventListener('load',() => {
        navigator.serviceWorker.register('serviceworker.js')
        .then(() => {console.log('Service Worker Registered successfully');})
        .catch((e) => {console.log('Service Worker Registeration failed' + e);});
     });
}

You can view the registered service worker from Google chrome dev tools, inside application tab.

serviceworker

STEP 4:

Once the service worker is registered, we can cache all the App shell (static resources) required for the web app using CACHE API.


const CACHENAME = 'APPShell';
const DynamicCacheName = 'news';
const staticAssets = ['/','/site.css','/app.js'];

self.addEventListener('install', async function (event) {
    //Cache all the internal static assets (AKA AppShell) on the installation of service worker
    console.log('caching started');
    event.waitUntil(
      caches.open(CACHENAME).then(async function(cache){
        for(var i=0 ; i< staticAssets.length; i++)
        {
          await cache.add(staticAssets[i]);
        }
        console.log('Cache successful');
      })
    );
  });

Once adding the cache, in the application tab of chrome dev tools you will be able to find it.

cache

STEP 5:

Once all the static resources are cached, lets use FETCH API to serve the requests based on the availability. We will be using Cache First Approach and Network First Approach here to get the resources from cache.

Cache First Approach:

To get the app shell of our web app we will be fetching it from the cache if exists or we will fetch it from the internal server.


  self.addEventListener('fetch', event => {

    const request = event.request;
    const url = new URL(request.url);
    console.log('entering fetch');
    if (url.origin === location.origin) {
      //Use cache first approach for the internal requests
      event.respondWith(CacheFirst(request));
    }
   else {
     //Use network first approach for the requests from different domain
      event.respondWith(NetworkFirst(request));
   }
  });

  //Primarily gets the file from cache, if not found gets the files from server.
  async function CacheFirst(request) {
    const cachedResponse = await caches.match(request);
    return cachedResponse || fetch(request);
  }


Network First Approach
:

To get the news from NEWS API, we will be using Network first approach as we have to fetch the latest news always, if incase the user is offline then the cached news will be displayed to the user instead of network not found screen on the browser (Usually the dinosour running page on chrome browser).


//Primarily tries to connect to the network to get latest records, if not fetches from the cache
  async function NetworkFirst(request) {
    const dynamic = await caches.open(DynamicCacheName);
    try {
      const networkResponse = await fetch(request);
      //Use clone to cache it as the response can be consumed only once
      dynamic.put(request, networkResponse.clone());
      return networkResponse;
    } catch (e) {
      const cachedResponse = await dynamic.match(request);
      return cachedResponse || await caches.match('./noFound.json');
    }
  }

 

After adding cache implementation, you can put your browser on offline mode and check how your web app responds. The static and dynamic resources will be loaded from the cache making the application work offline, instead of showing an error screen.

cache service worker.PNGIn a non PWA you will probably see the following screen.

not available.PNG

STEP 6:

We are almost done, now lets add a manifest file to our app to make it a completely functional PWA. You can autogenerate this manifest file from https://app-manifest.firebaseapp.com/


{
  "name": "PWA News App",
  "short_name": "News",
  "theme_color": "#2196f3",
  "background_color": "#2196f3",
  "display": "standalone",
  "orientation": "portrait",
  "Scope": "/",
  "start_url": "./index.html",
  "icons": [
    {
      "src": "images/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    }
  ],
  "splash_pages": null
}

You will can see the loaded manifest file for our web app from the below image.

1.PNG

As previously mentioned PWAs can run offline and also gives us an option to add to home screen. If you add it to the home screen you can enter the app next time with the single click also with app like user experience. This is how it will look like out of box.

homescreen.png

Tadaaa..!!! we have successfully created a simple PWA that can be added to the home screen and with offline functionality. Hope this article had helped you in understanding the basics of PWA. You can get the full source code from Github and as always do reach me out for any queries.

Advertisements

2 comments

  1. Nice work da goku. Happy to see someone we know are spreading knowledge, Which is a real good term. Keep doing such good things and keep growing.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s