Your first progressive web app: Part 1

Finds this amazing new app.
Clicks the download button.
“Insufficient storage available”

Not THAT message again! We all have been through this frustration which makes it very inconvenient to have all our favorite apps on one device.

Well worry no more, here is progressive web apps to your rescue. A progressive web app is a usual browser site which can behave like a native mobile application but without the need for it to be downloaded to your mobile. And not only this, it has some other amazing features as well.

  1. Offline usage with caching strategies.
  2. Background sync to upload data offline.
  3. Push notifications just like native apps to keep your users engaged.

The google docs are a great place to get started with the basics. I will be covering everything right from basics like configuration to core topics like best practices for achieving offline implementation. This is the first article in a four part series. At the end of this series you will be able to:

  1. Configure a service worker file and cache files[PART 1].
  2. Upload text data offline using background sync [PART 2].
  3. Upload images offline using background sync [PART 3].
  4. Handle offline usage with the help of caching strategies [PART 4].

CONFIGURATION

Right now you must be thinking:

“So all these features must require a ton of code right?”

Well, ease your tension since all that we need is a service worker file. This one single file will handle all events right from basics like file caching to more complex events like background sync. We can call it one single controller for all our progressive web app functionalities.

STEP 1 Lets create our service worker file “sw.js”. An important point to note here is that our service worker file is generally created in the root directory of our app. This is because it will only have access to files in the directory in which it is created and to the files in the sub directories.

STEP 2 Add this code to your “sw.js” file.

var dataCacheName = ‘dataCache’;
var cacheName = ‘fileCache’;
var filesToCache = [
 ‘/’,
 ‘/index.html’,
 ‘/ang1.js’
];
self.addEventListener(‘install’, function(e) {
 console.log(‘[ServiceWorker] Install’);
 e.waitUntil(
 caches.open(cacheName).then(function(cache) {
 console.log(‘[ServiceWorker] Caching app shell’);
 return cache.addAll(filesToCache);
 })
 );
});
self.addEventListener(‘activate’, function(e) {
 console.log(‘[ServiceWorker] Activate’);
 e.waitUntil(
 caches.keys().then(function(keyList) {
 return Promise.all(keyList.map(function(key) {
 if (key !== cacheName && key !== dataCacheName) {
 console.log(‘[ServiceWorker] Removing old cache’, key);
 return caches.delete(key);
 }
 }));
 })
 );
 return self.clients.claim();
});
self.addEventListener(‘fetch’, function(e) {
 console.log(‘[Service Worker] Fetch’, e.request.url);
 e.respondWith( 
 caches.match(e.request).then(function(response) {
 return response || fetch(e.request);
 },
 function(response)
 {
 return response;
 }
 )
 );
});

All that we are doing here is defining a set a files which we want to cache for offline storage. When the service worker gets installed, these files will be added to the cache from where we can fetch them later.

Note that we have created two separate caches here: “dataCache” and “fileCache”. As the name suggests, we will be storing our files in the file cache. The purpose of this is to decrease load times and to access files offline. In our data cache, we store all our AJAX responses so that we can serve content offline. In this part we our only caching files so that we can launch our app offline.

In the “fetch” event handler we check our file cache for the file that was requested. If it is available we return it directly from the cache. If it is not, then we give a call to the network to fetch the file. This is a if not cache then network caching strategy.

STEP 3 Now we need to register our service worker file in our view. The service worker will be installed on the starting page of our app. Since it has been defined in the root directory of our app, it will be accessible to all the files. Add the following script to our angular file “ang1.js” which is the controller for our view “index.html”.

var app = angular.module(‘myApp’, []);
 
app.controller(‘myCtrl’, function($scope,$http,$q,$document) {
});
if (‘serviceWorker’ in navigator) {
 console.log(‘Reached 1’)
 $(document).ready(function() {
 console.log(‘Reached 2’)
 console.log();
 navigator.serviceWorker.register(‘/sw.js’)
 .then(registration => navigator.serviceWorker.ready)
 .then(registration => {
 console.log(‘Reached 4’)
 })
 .catch(err => {
 console.log(“Reg failed!”)
 });
 });
}

STEP 4 Now we need to define our view “index.html”. For now, all we will do is define the angular controller and include the angular script. We also include our “manifest.json” file which I will explain next. Add the following code to “index.html”.

<html>
<head>
 <link rel=”manifest” href=”/manifest.json”>
 <script src=”https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
 <meta charset=”utf-8">
 <meta name=”viewport” content=”width=device-width, initial-scale=1">
 <script src=”https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
 <script src=’../ang1.js’></script>
</head>
<body>
<div ng-app=”myApp” ng-controller=”myCtrl”>
Welcome to the Home Page!
</div>
</body>
</html>

STEP 5 Well we are almost there now. All that we need to add is a “manifest.json” file. So what does this file do? Well just like a service worker file makes our app behave native-like, a manifest file makes our app look native-like.

  1. It hides the URL tab in the browser to display the app in full-screen mode.
  2. It defines the icon-image for our app to launch.
  3. It defines the display-name for our app to launch.
  4. It also defines the starting page of our app.

Add the following code to “manifest.json”.

{
 “name”: “PWA App”,
 “short_name”: “PWA App”,
 “icons”: [{
 “src”: “image1.png”,
 “sizes”: “128x128”,
 “type”: “image/png”
 }, {
 “src”: “image2.png”,
 “sizes”: “144x144”,
 “type”: “image/png”
 }],
 “start_url”: “/index.html”,
 “display”: “standalone”,
 “background_color”: “#3E4EB8”,
 “theme_color”: “#2F3BA2”
}

Please edit the image paths to add any image you like for your app icon. You can have different images for different devices which will be decided by the size you specify for that image.

And that’s it! You are ready to launch your first progressive web app.


LAUNCH APP

Now that are code is ready, lets launch our app.

  1. I am assuming you have hosted this code either locally or online. You can also use Web Server for Chrome.
  2. Navigate to the home page: https://url/index.html using Chrome browser.
  3. Please make sure you use https (not http) since we need a secure location for our service worker to register.
  4. Check your console. The golden message here is: “Reached 4”. If you see this message in the console then everything went well and the service worker has been registered.
  5. Inspect the site and go to the Application->Cache Storage . You should be able to the cached files here in “fileCache”.
  6. Now go to settings in the top right corner in your browser and select “Add to Home Screen”.
  7. Now close the browser and you should be able to see an icon in your home screen with the image you added to your “manifest.json” file.
  8. Go ahead and try launching the app from the home screen and see the magic happening. Also try launching offline.

I hope you enjoyed this article. I will add links to further articles here when they are ready.

Here is the GitHub repository for this series: