Progressive Web Apps, aka PWAs, are a trending topic these days, everyone has something to say about them. That's because they are a nice way of distributing apps without publishing on any App Store. I'm skipping all the lack of features when comparing them to native apps, if you have a web app that somehow has to be used in mobile devices and it just requires some API calls there may be no need for a native app, and it can be published as an PWA.
Most of the tutorials out there state that it's very easy to turn your Single Page Application, aka SPA, into a PWA. Absolutely TRUE, but also most of them are (IMHO) incomplete. So here I am, trying to point your search towards the most valuable resources, and adding some of my own.
This article is written in a keep reading, copy pasting enabled ahead mode, so if you're looking for a full working copy & pastable example code, just keep reading. Read, understand, copy, paste, rinse, repeat…
How to turn an existing App into a PWA
Easy, you need:
- A service worker
- A manifest.json file
The service worker defines caching strategies for every app resource, including scripts, images, API calls or other routes; and event handling for the own app events. It can also handle custom events for everything that is not meant to be executed on the app itself, but on the background.
Here my service-worker is using an old (but still useful) Google tool called the sw-toolbox. You will probably have to install it; I did it using bower (line 8 uses the bower_components path) running the command:
bower install — save sw-toolbox
Google has updated the tool, and now they suggest using Workbox, which is almost the same.
The manifest.json file includes some general app information, name, icons, colors, and display mode.
Also you need to register the service worker, and include the manifest.json in your app (usually on the index.html file):
See lines 17, and the script starting on line 30. I'll come back to why the script is not on the head section in a while.
Until here there's nothing new, almos every tutorial gives the same advices. But here we are missing the "How to show an install app message" on both Android and iOS.
How to show an "Install app" message (Android)
Android handles this quite out-of-the-box. As Chrome is the best supported browser, I will refer to Chrome only (no Opera, nor Firefox, nor Samsung browser, or any other; highly biased article…. ehm… ok maybe). Since Chrome version 68, Chrome detects PWA enabled web apps and shows an install banner at the bottom called the mini-info bar. It doesn't ask if you (developer, not user) want it to display that banner or not, it just does. IMHO annoying, and no choice to change that behavior. Click on it and there you have it, a nice "Install this app" message.
Anyway, for Chrome versions before 68, there's a a nice piece of code that allows us to defer the installation, lets say until certain button is clicked.
If you want to test this script on your own PWA using desktop Chrome, you'll have to enable PWA flags. This is done navigating to chrome://flags , searching for pwa and enabling all options.
Anyway, this is still incomplete, because the DOM object with the ad2hs-prompt class is not present on any tutorial (not even in Google's). So for your copy & pasting pleasure, here's the code:
The secret is listening to the beforeinstallprompt event (line 86) and then triggering the prompt() function (line 63). This is the nice blue button with "Install web app" text on the main image in this article.
But… this is still incomplete. What about iOS?
How to show an “Install app” message (iOS)
Currently (January 2019) iOS has no support for PWAs' installation prompt, but it does have support for installing a Web App in the Home screen. This is achieved by clicking the Share button at the bottom and then scrolling to the Add to Home Screen button.
So, as there's no capability of detecting if the PWA has been already installed, nor triggering this actions programatically, our only choice is to detect if iOS, and detect the standalone mode.
There's a very good tutorial on how to make PWAs iOS friendly, but again the code is imcomplete.
First, detect if iOS and navigation mode, and based on that information do something:
The do something part must be something like showing the Android defer installation button, but just showing some instructions to manually do the Add to Home Screen stuff.
So completing the script (and only the script):
Again this code is incomplete, and almost no change from NetGuru's tutorial. There's no DOM object with ios-prompt class. So I'm completing the previous Android index.html with this code, and, as an extra, the ios-prompt:
As you can see this is why the service register script is not on the head. All PWA code is on the same script, as the showIosInstall() function is executed once the page loads, the .ios-prompt div must exist prior trying to assign any display styles (otherwise you'll get a style doesn't exist on null error).
But Netguru also speaks of splash screens and icons. That's right, our iOS enabled PWA is not ready if it doesn't have a nice icon, or a splash screen.
So here's the trick:
- Make a nice looking icon for your App. A large 2048x2048 PNG will do.
- Generate all icons using https://realfavicongenerator.net
- Unzip the .zip file in some folder and copy all apple-touch-icon* files to your app's root folder.
- Paste this in the head section of your index.html file
<!-- place this in a head section -->
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="60x60" href="apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="76x76" href="apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="120x120" href="apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="152x152" href="apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon-180x180.png">
That should make it for the icons. But we still need the splash screen. SO here's another trick:
- Make a nice looking splash screen for your app. Think in portrait layout, so perhaps your icon image will not work (by the way, make it nicer than the icon, you have more space to draw).
- Upload your splash image to https://appsco.pe/developer/splash-screens to generate the required files.
- Unzip the .zip file in some folder and copy all apple-launch* files to your app’s root folder. You already have a mess in your root folder so nevermind.
- Paste this in the head section of your index.html file, below the icon lines you recently pasted:
<!-- iPhone X (1125px x 2436px) -->
<link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)" href="apple-launch-1125x2436.png">
<!-- iPhone 8, 7, 6s, 6 (750px x 1334px) -->
<link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)" href="apple-launch-750x1334.png">
<!-- iPhone 8 Plus, 7 Plus, 6s Plus, 6 Plus (1242px x 2208px) -->
<link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3)" href="apple-launch-1242x2208.png">
<!-- iPhone 5 (640px x 1136px) -->
<link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)" href="apple-launch-640x1136.png">
<!-- iPad Mini, Air (1536px x 2048px) -->
<link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)" href="apple-launch-1536x2048.png">
<!-- iPad Pro 10.5" (1668px x 2224px) -->
<link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)" href="apple-launch-1668x2224.png">
<!-- iPad Pro 12.9" (2048px x 2732px) -->
<link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)" href="apple-launch-2048x2732.png">
Be aware of the file paths in every href, because it may not match your app's path.
And to finish, you may have noticed there're 2 files missing:
You may have to rename them, and place them in the assets/imgs/ folder.