How to make an application out of the site and put it on Google Play in a few hours. Part 1/2: Progressive Web App

How to make an application out of the site and put it on Google Play in a few hours. Part 1/2: Progressive Web App




Probably all people close to web development have already heard about Progressive Web App. Still would! This technology has practically balanced the web and mobile development in terms of product distribution and user involvement.

Yes, a modern frontend, written, for example, on React, works as an application. But only this application is downloaded to the browser and launched from it. This is a huge handicap, which has always had a mobile development. Let's think, than from the point of view of a regular user, the “application” is different from the “site”. Immediately comes to mind that the application on the phone, and the site on the computer. But there is a mobile browser, so the site is on the phone too. Then there are 3 significant differences:

  1. The application icon is on the main screen of the smartphone.
  2. The application opens in a separate window.
  3. The application sends push notifications.

All 3 points are removed thanks to Progressive Web App or PWA. Now, accessing the site from a mobile browser, we can “download” it, after which we will see an icon on the main screen. In addition, a splash screen appears on startup, just like in mobile apps, and if you wish, you can configure push notifications to be sent.

And it would seem, everything is fine! But alas, for more than 10 years of the mobile era, users have become too accustomed to looking for apps on Google Play and the App Store. Breaking user habits is an ungrateful business, and therefore the guys from Google (by the way, Google is the developer of PWA) decided that if the mountain does not go to Magomed, then ... In general, most recently, on February 6, 2019, they ensured you use Trusted Web Activities to display web apps on Google Play.

The two-part article will explain how to get from a regular website to an application on Google Play in just a few hours. All this will be shown on the example of a real service - Skorochets .

Lighthouse


We have a mobile layout website at the entrance:


The first step is to install the Lighthouse extension in Google Chrome on your work computer. It is a tool for analyzing sites in general and for verifying compliance with the Progressive Web App standard in particular.

Next, open our website, combat or running locally, and generate a report for it with the help of Lighthouse:


In the Progressive Web App section of the report you should see something like the following:



Pay attention to the Installable section. Firstly, if you run the site locally, and you have to do this during development and testing, then you need to use the localhost domain and no other. This will satisfy the “Use HTTPS” requirement, or rather Lighthouse will simply close its eyes to it, and you will be able to fully test your PWA.

In addition to the HTTPS requirement for our application to become PWA and become installable, we need to connect the service worker and the Web app manifest to the site. Let's do it.

Service worker


Technology service workers allows your site to be online even when the server is unavailable. It is such an intermediary between the client and the server that intercepts each request and in which case it slips data from the cache as a response.

For PWA to work, a basic implementation of a service worker is sufficient, which looks like this:
service-worker.js
 //Must be true in production
 var doCache = true;
//cache name
 var CACHE_NAME = 'my-pwa-cache-v2';
//Clears old cache
 self.addEventListener ('activate', event = & gt; {
  const cacheWhitelist = [CACHE_NAME];
  event.waitUntil (
  caches.keys ()
  .then (keyList = & gt;
  Promise.all (keyList.map (key = & gt; {
  if (! cacheWhitelist.includes (key)) {
  console.log ('Deleting cache:' + key)
  return caches.delete (key);
  }
  }))
  )
  );
 });
//'install' is called as soon as the user first opens PWA
 self.addEventListener ('install', function (event) {
  if (doCache) {
  event.waitUntil (
  caches.open (CACHE_NAME)
  .then (function (cache) {
//Get the data from the manifest (they are cached)
  fetch ('/static/reader/manifest.json')
  .then (response = & gt; {
  response.json ()
  })
  .then (assets = & gt; {
//Open and cache the necessary pages and files.
  const urlsToCache = [
  '/app/',
  ........
  '/static/core/logo.svg*',
  ]
  cache.addAll (urlsToCache)
  console.log ('cached');
  })
  })
  );
  }
 });
//When the application is running, the service worker intercepts requests and responds to them with data from the cache, if any
 self.addEventListener ('fetch', function (event) {
  if (doCache) {
  event.respondWith (
  caches.match (event.request) .then (function (response) {
  return response ||  fetch (event.request);
  })
  );
  }
 });  

Handlers for three events are implemented here: install , activate and fetch . As soon as the user opens the site where there is a service worker, the install event will be triggered. This is the procedure for installing a service worker in the user's browser. In its handler in the array urlsToCache you can specify the pages of the site that will be cached, including statics. Then it calls activate , which clears the resources used in the previous version of the service worker script. And now, when the service worker has been successfully installed, it will intercept each fetch event and search the cache for the requested resources before following them to the server.

To make it all work, you need to add a script to register the service worker in the html files. Since Skorottets is a one-page application (SPA), it has one single html, which after adding the specified script looks like this:

index.html
  & lt;! DOCTYPE html & gt;
 & lt; html lang = "en" & gt;
 & lt; head & gt;
  & lt; meta charset = "UTF-8" & gt;
  & lt; title & gt; Skorotets & lt;/title & gt;


 & lt;/head & gt;
 & lt; body & gt;
  & lt; div id = "root" & gt; & lt;/div & gt;
  & lt; script src = "/static/build/app.js" & gt; & lt;/script & gt;

  & lt; script & gt;
  if ('serviceWorker' in navigator) {
  window.addEventListener ('load', function () {
  navigator.serviceWorker.register ('/service-worker.js'). then (function (registration) {
//Registration was successful
  console.log ('ServiceWorker registration successful with scope:', registration.scope);
  }, function (err) {
//registration failed :(
  console.log ('ServiceWorker registration failed:', err);
  }). catch (function (err) {
  console.log (err)
  });
  });
  } else {
  console.log ('service worker is not supported');
  }
  & lt;/script & gt;
 & lt;/body & gt;
 & lt;/html & gt;  

The navigator.serviceWorker.register ('/service-worker.js') function takes as its argument the URL where the service worker file is located. It does not matter how exactly the file is called, but it is important that it is located in the root of the domain. Then the service domain will be the entire domain, and it will receive fetch events from any page.

Thus, by placing the service worker file at skorochtec.ru/service-worker.js and adding the necessary script in html, we get the following picture in the Lighthouse report:



If we compare with the previous report, now we are satisfied with the second point and the site responds with 200 even offline, and in the 5th paragraph we see that the service worker was found, but the start page is missing. Information on the start page and not only is indicated in the Web App Manifest, let's add it!

Web App Manifest


Manifest provides information about our application: short and long name, icons of all sizes, start page, colors and orientation.

manifest.json
  {
  "short_name": "Scoring",
  "name": "Scorotte",
  "icons": [
  {
  "src": "/static/core/manifest/logo-pwa-16.png",
  "sizes": "16x16",
  "type": "image/png"
  },
  {
  "src": "/static/core/manifest/logo-pwa-32.png",
  "sizes": "32x32",
  "type": "image/png"
  },
  {
  "src": "/static/core/manifest/logo-pwa-48.png",
  "sizes": "48x48",
  "type": "image/png"
  },
  {
  "src": "/static/core/manifest/logo-pwa-72.png",
  "sizes": "72x72",
  "type": "image/png"
  },
  {
  "src": "/static/core/manifest/logo-pwa-96.png",
  "sizes": "96x96",
  "type": "image/png"
  },
  {
  "src": "/static/core/manifest/logo-pwa-144.png",
  "sizes": "144x144",
  "type": "image/png"
  },
  {
  "src": "/static/core/manifest/logo-pwa-192.png",
  "sizes": "192x192",
  "type": "image/png"
  },
  {
  "src": "/static/core/manifest/logo-pwa-512.png",
  "sizes": "512x512",
  "type": "image/png"
  }
  ],

  "start_url": "/app/",
  "background_color": "# 7ACCE5",
  "theme_color": "# 7ACCE5",
  "orientation": "any",
  "display": "standalone"
 }  

The last variable indicates that it will be a separate application. The manifest file must be located on the site (not necessarily in the root) and connect it to html:

index.html
  & lt;! DOCTYPE html & gt;
 & lt; html lang = "en" & gt;
 & lt; head & gt;
  & lt; meta charset = "UTF-8" & gt;
  & lt; title & gt; Skorotets & lt;/title & gt;

  & lt;! - Add manifest - & gt;

 & lt; link rel = "manifest" href = "{% static" core/manifest/manifest.json "%}" & gt;

  & lt;! - Tell the browser it's a PWA - & gt;
 & lt;/head & gt;
 & lt; body & gt;
  & lt; div id = "root" & gt; & lt;/div & gt;
  & lt; script src = "/static/build/app.js" & gt; & lt;/script & gt;

  & lt; script & gt;
  if ('serviceWorker' in navigator) {
  window.addEventListener ('load', function () {
  navigator.serviceWorker.register ('/service-worker.js'). then (function (registration) {
//Registration was successful
  console.log ('ServiceWorker registration successful with scope:', registration.scope);
  }, function (err) {
//registration failed :(
  console.log ('ServiceWorker registration failed:', err);
  }). catch (function (err) {
  console.log (err)
  });
  });
  } else {
  console.log ('service worker is not supported');
  }
  & lt;/script & gt;
 & lt;/body & gt;
 & lt;/html & gt;  

Let's analyze the Lighthouse site again:



Hooray! Now we have not just a website, but a Progressive Web App! You may have noticed that the download speed has increased dramatically. It has nothing to do with what we did, I just replaced the development assembly of the React-application with production so that the report looked as beautiful as possible.

Well, go to the site from mobile Chrome and what do we see?


Yes! You can open the champagne! Adding an application to the main screen:



As a bonus, we get a splash screen on startup, which is collected from the names specified in the manifest name, background_color and 512x512 icons in the icons array:


Unfortunately, the text color is automatically selected, which in Skorchette’s case breaks the style a bit.

Well, the application itself:


Restrictions


At the moment, PWA is supported only in Chrome and Safari (starting with iOS version 11.3). Moreover, Safari supports this technology "quietly." The user can add the application to the main screen, but there is no message about it, unlike Chrome.

Useful tips and tricks


1. Safari Installation Offer


Since Apple did not do this (we hope that we have not done it yet), we have to implement it with our own hands. It turns out like this:


Implemented with the following javascript:

  const isIos = () = & gt;  {
  const userAgent = window.navigator.userAgent.toLowerCase ();
  return/iphone|ipad|ipod/.test (userAgent);
  };
//Check if the application is open separately or in the browser
  const isInStandaloneMode = () = & gt;  ('standalone' in window.navigator) & amp; & amp;  (window.navigator.standalone);



//If the application is open on iOS and in the browser, we suggest installing
  if (isIos () & amp; & amp;! isInStandaloneMode ()) {
  this.setState ({isShown: true});//Using React as an example
  }  

2. Tracking Installations


This works only in Google Chrome. You need to add a script to the html that catches the appinstalled event and, for example, send a message to your server about this:

  & lt; script & gt;
  window.addEventListener ('appinstalled', (evt) = & gt; {
  fetch (& lt; your_url & gt ;, {
  method: 'GET',
  credentials: 'include',
  });
  });
 & lt;/script & gt;
  

3. Choosing the right start_url


Be sure to take care that the url of all pages of the application are a continuation of the start_url specified in the manifest. Because if you specify "start_url": "/app/" , and then the user navigates to the page, say, "/books/", the browser address bar and the entire user experience will immediately appear will break. In addition, the person will feel deceived: he thought that he was using the application, and this is a disguised browser. And even theme_color from the manifest, which will color the browser interface in your corporate color, will not save.

In the case of Skoroshtetsa, all pages related to the application begin with/app/, therefore such incidents do not occur.

What's next?


Well, now you know how to get to the user on the main screen of the smartphone through your website. But this is only one of the doors, and most likely not the front door. The second part will explain how to enter through the front door: you will learn how to put your progressive web application on Google Play.

Useful Links


Source text: How to make an application out of the site and put it on Google Play in a few hours. Part 1/2: Progressive Web App