Last time, we got into the nitty-gritty on how to make your web application into a Progressive Web Application (PWA to its friends). I promised we’d dig even deeper this time, and show you how to make your web app a little more ‘native’ on Android – and how to deal with iOS Safari’s special snowflake syndrome.
Manifestly So
The key to gaining first-class citizen status on Android for our PWA is the Manifest. This allows us to declare certain details about our PWA, and how we’d like it to be handled by the browser. We won’t go into all the possibilities – see the MDN link before for all of the details – but instead look at a few common scenarios, namely:
- You want to specify a certain application name, and ‘friendly’ short form of that name for display in settings and on the home screen;
- Your app can be installed to the Home Screen from the web app site, and will automatically prompt the user to do so;
- You want to specify some values for things like background color, theme color, and icons, to control the way the app will look on the home screen, and how it will look when it’s loading;
- You want to lock the app into a certain display orientation.
To that effect, here’s an example Manifest, which we’ll be dissecting momentarily:
{
"short_name": "App Name",
"name": "Long App Name for Settings Or Lock Screen",
"background_color": "#hexcode",
"theme_color": "#hexcode",
"display": "standalone",
"orientation": "portrait",
"icons": [
{
"src": "/static/img/icon180x180.png",
"type": "image/png",
"sizes": "180x180"
},
{
"src": "/static/img/icon192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/static/img/icon512x512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": "/"
}
So, let’s go through this line-by-line.
The short_name
and name
properties are fairly self-explanatory. The short_name
will be displayed anywhere the OS decides the name
won’t fit. These could, of course, simply be the same string. How the OS deals with short_names that are still too long is up to the OS, but you can expect truncation of your name, so try and keep the short_name pithy.
background_color
is an important one, as this is going to get used by the OS to generate the ‘splash screen’ that the user sees when they load your application. If you specify icons, the background-color will be displayed behind the chosen icon.
theme_color
is a little more ambiguous, as what’s done with it is up to the OS, and might change; right now, specifying it will determine what colour surrounds the application and is used for it in the task switcher.
display
is another big one – it determines how ‘app-like’ your PWA is going to appear. If you specify ‘standalone’, as we have here, it should look and feel like a standalone application – the browser chrome should be excluded, but items like the status bar can be displayed. This is suitable for a LoB-type application. There’s also ‘fullscreen’, which gives you every last pixel – perfect for games – ‘minimal-ui’, which incorporates some of the browser chrome (how much depends on the browser), and ‘browser’, which leaves all of the browser chrome around your PWA intact, as though it were being viewed in the browser.
orientation
is another important one for an app – it allows you to specify that the application should be locked to ‘portrait’ or ‘landscape’ orientation, amongst a host of other possible values that you can see detailed in the MDN doc linked above.
icons
is, as mentioned, the icons that will be used for display on the home screen and when generating the ‘splash screen’ the user will see when loading the application. You should specify an array of options to handle different screen sizes and the different contexts in which an icon will be used (e.g. task switcher, home screen, splash screen, etc.). Relative URLs are resolved relative to the location of the manifest.json file (more on that in a minute); you’ll notice we’re using all PNG files for our icons, but they don’t have to be… unless you’re also planning to use them with iOS (and more on that in a minute as well).
Finally, start_url
is where we want the PWA to be when it loads from the home screen (as it’s possible that the user could have chosen to add the PWA to the home screen from any url on your web app, and without this start_url
specified, that would become the start_url for your PWA).
All of these details should live in a file called manifest.json
and should be somewhere your web server can serve static files from, as you’ll be linking to it in the head of your base html file.
Somewhere in the
<head>
of your html index, you should add the following meta tag:
<!-- PWA Manifest -->
<link rel="""manifest" href="/path/to/manifest.json">
That’s it for Android, you’re ready to go!
Now, as long as you’re adding meta tags, let’s look at the slew of meta tags needed for PWA support on iOS.
Snowflakes aren’t that Special
So, by now you’re probably pretty excited – you’ve basically got this PWA thing working, right? Well, sure – unless you need to support iOS.
Because iOS doesn’t support Service Worker. Which means it doesn’t support PWAs.
But wait! No need for that wail of despair (or that sixth whiskey) – I haven’t lead you down here just to dash all your hopes. iOS doesn’t support Service Worker yet, but they’re working on it. Whether they’ll also support the manifest functionality is uncertain, but Service Workers are really the beating heart of PWAs, so one day soon, all the work you’ve put in so far will pay off on iOS.
In the meantime, even though we can’t have a full-fat PWA, we can get pretty close with iOS’s Web Clips functionality. That link will give you the details, but let’s look at an example. All of the below will go into the “ of your html document.
<!-- iOS specific headers for 'web app' behaviours and look-and-feel. -->;
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Your app name">
<!-- iOS Home Screen Icon -->
<link rel="apple-touch-icon" href="/static/img/yourAppIcon.png">
<!-- Splash screens for iOS - these did not work in iOS 9/10, but are now working in iOS 11+ - see https://forums.developer.apple.com/thread/23924. -->
<!-- iPhone 7 Plus &amp;amp; 6/s Plus portrait startup image -->
<link href="{% static 'img/launchScreen1242x2208.png' %}" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3)" rel="apple-touch-startup-image">
<!-- iPhone 7 &amp;amp; 6s portrait startup image -->
<link href="{% static 'img/launchScreen750x1334.png' %}" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
<!-- iPhone 6 portrait startup image -->
<link href="{% static 'img/launchScreen750x1294.png' %}" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
<!-- iPhone 5, SE portrait startup image -->
<link href="{% static 'img/launchScreen640x1136.png' %}" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
Whew! Of course, a lot of that space is taken up by the various startup images – and if we wanted to display ‘landscape’ startup images as well, it would be twice as long. The launch screen image size you will want can be drawn from the Apple Human Interface Guidelines document – the specific media queries to use for each is magic culled from some trial and error – you will likely have to perform some testing of your own.
Above those, you’ll see the meta tags that make our web app ‘PWA-like’ on iOS – particularly apple-mobile-web-app-capable
. Include these, and your users can use the Web Clip capability in iOS Safari to ‘Save to Home Screen’ your site. You’ll want your icon specified as well – it can be any square resolution (e.g. 180×180 pixels) – and you’ll want to specify the application name with the apple-mobile-web-app-title
tag – otherwise the contents of the title
tag are used.
Finally, take a look at the apple-mobile-web-app-status-bar-style
tag – we’re using it to declare that we want the status bar to overlap our application, rather than push it down. The other options – which you can find in the Web Clip specification link, above – will set it’s colour to white or black, and push your content down below it. Unfortunately, there’s no way to gain every last pixel yet.
Cache This
There’s a substantial item missing from the iOS support set that we have on Android – caching for offline support. No Service Workers, no offline support. Well, now what?
Luckily, if this is the make-or-break item, we can make it – using the older Application Cache API. Using the application cache is a topic unto itself, so the only thing I’ll say here is that you’ll need to be careful to only serve the application manifest to iOS devices (and you’ll want to check that you’re only serving it to iOS devices without Service Worker support, for future-proofing purposes).
Application Cache and Service Workers don’t play nice together (yet – watch this Chromium bug for some details on progress for that project). One day, the W3C suggested behaviour is that browsers will ignore and/or garbage-collect the application cache when a service worker is registered and takes over caching, but that day is not yet here, and you don’t want two competing technologies caching things and producing unexpected results.
Are we there Yet?
This time, we really are – together with Part 1, you’ll now have a functioning web application that can also be installed to the home screen on Android like a native application, and almost-like-native on iOS; and you’re ready for the brave future where iOS becomes fully PWA-capable; AND you have an application that can still be accessed anywhere, by anyone via the Internet.
Not a bad day’s work. Go get yourself a drink.
Header image by david/hoodo youdo, licensed under Creative Commons BY-NC-ND 2.0.