Don’t Worry, Be Appy

A presentation at An Event Apart Spring Summit in April 2022 in by Aaron Gustafson

Slide 1

Slide 1

Hi there, my name is Aaron Gustafson Pronouns he/him/his Land acknowledgement WaSP Microsoft - Web Standards & a11y Editor – Web App Manifest and related specs @ W3C

Slide 2

Slide 2

For as long as I can remember the web has been framed in opposition to traditional software development, but I never cared for that framing. I’ve always felt the two can and should coexist. I also believe the web is highly capable as a platform for delivering software and it just keeps getting better.

Slide 3

Slide 3

I feel like the web vs. apps sentiment reached a boiling point with the advent of the iPhone, which is kinda funny as the first apps available on iOS were actually websites. This is a screenshot of a web app I created way back in 2008, just before the iPhone was released. Those were the days when web apps were the only way 3rd party developers could get an icon on the iPhone home screen. But Apple wasn’t the only company to see value in using web tech to develop apps.

Slide 4

Slide 4

There were a ton of frameworks that enables folks to write web code and package it as an installable binary. And several OSes either enabled web apps to co-exist with their platform-specific counterparts or made the web the sole way to deliver apps on their platform. In many of these cases, however, the web code needed to be packaged or compiled in some way to make it intelligible to the host operating system. It got some folks wondering: could the browser enable developers to skip that step entirely?

Slide 5

Slide 5

And so Progressive Web Apps were born. As a concept, PWAs marry the responsive nature of web design with a host of newly-minted APIs and browser features to enable websites to become full-fledged apps.

Slide 6

Slide 6

I think one of the things about the term progressive web app that trips folks up is the “web app” bit. What is a web app anyway? Jeremy Keith likes to say Like obscenity and brunch, web apps can be described but not defined.” “

Slide 7

Slide 7

So whenever you think about PWAs, I’d like you to think about them as websites with special powers.

Slide 8

Slide 8

PWAs are just normal websites that have been enhanced in a few key ways to improve the user experience they deliver (and that happen to make them more “app like.”

Slide 9

Slide 9

Any website can (and probably should be a PWA). If you’d like to explore how your site could benefit from being a PWA, I wrote about this topic for A List Apart.

Slide 10

Slide 10

Slide 11

Slide 11

While not everything I’ll be talking to you about today requires that your site be a PWA, almost everything I’m going to talk to you about today requires a secure connection.

Slide 12

Slide 12

If you are not super-familiar with how to build a PWA, have no fear, nothing I will be discussing today requires intimate knowledge of building PWAs. If you’d like more info though, there is an awesome article series you should check out that will help you get up to speed quickly: 30DaysOfPWA. I will be showing code in this talk, but I promise to step through it bit-by-bit so you can follow along more easily.

Slide 13

Slide 13

I want to start by talking by talking about installation. That may not seem super-interesting, but it’s actually a pretty important starting place.

Slide 14

Slide 14

When I talk about installation in this context, I want to focus on what we need to do to make browsers see our websites as installable. Here you can see that insiders.vscode.dev triggers a little badge to be shown in the address bar. If it’s the first time you’ve gone to a site, there might also be some accompanying text saying something like “App available.” That’s a pretty cool way to build some awareness that your PWA exists. So how do we get that?

Slide 15

Slide 15

First we need that Web App Manifest file.

Slide 16

Slide 16

A Manifest is a big JSON file filles with metadata about your app. If this looks like jibberish to you, have no fear, I’ll walk you through what a real web app manifest looks like.

Slide 17

Slide 17

Before we get there though, we need to connect this file to our site via HTML, using the link tag

Slide 18

Slide 18

The relationship of the linked document is ”manifest”

Slide 19

Slide 19

And the href should point to your JSON file.

Slide 20

Slide 20

Everything is optional, but when we are looking at installation, these are the keys we should start with.

Slide 21

Slide 21

You can define the language & direction. If you have a multi-lingual app, you might consider providing multiple manifests tailored to each as there is—as of yet—no way of managing translations within the manifest (though we are working on it).

Slide 22

Slide 22

The default value of dir is auto, so you may not need it except in specific instances.

Slide 23

Slide 23

Name is the preferred name for the app.

Slide 24

Slide 24

Short name is also available and enables you to define a specific short option just in case the host operating system doesn’t support as many characters as you need for your app’s name.

Slide 25

Slide 25

This is the page that should open when your app is launched after install UAs may ignore it You can add a QS for analytics tracking, but don’t include unique tracking info for a user here! End users may override your setting

Slide 26

Slide 26

Lastly there’s the display mode.

Slide 27

Slide 27

Here’s what the different modes might look like. Any valid display type, apart from “browser” will be accepted for installation

Slide 28

Slide 28

So that’s the start, but there’s one final step we need to signal to browsers that they should prompt users to install our app

Slide 29

Slide 29

We need to give our app an icon (or offer several choices for the browser to pick the best one)

Slide 30

Slide 30

Icons are defined using an array of ImageResources

Slide 31

Slide 31

Here I’m presenting 3 options. Each is an ImageResource.

Slide 32

Slide 32

Each ImageResource is an object.

Slide 33

Slide 33

The src is the location of your source file (can be relative to the manifest URL) Any image format is possible, though SVG is not well supported for icons… yet (we’re actively working on it)

Slide 34

Slide 34

MIME declaration for the image to enable browser to select based on support

Slide 35

Slide 35

Space-separated list of dimensions (width x height) supported by the image. ICO files can have multiple, otherwise it should be the natural dimensions. Note: that’s an x not a multiplication sign, not an asterisk. Upper or lower is fine. It maps to the sizes value in HTML images. PWABuilder.com is a great resources for icon size recommendations, but generally folks want to see a 512x512 or larger icon.

Slide 36

Slide 36

Look up Here you can see the icon in use within macOS

Slide 37

Slide 37

The Manifest’s implementation of the ImageResource spec adds a purpose property Monochrome can also be used to switch an icon from light mode to dark mode, etc.

Slide 38

Slide 38

Slide 39

Slide 39

And there we have it: a Manifest ready for installation. Just one more thing to do…

Slide 40

Slide 40

The final requirement for installation is a Service Worker. Discussing Service Workers on their own could take a whole workshop, so I’m going to have to keep it short. For installation,

Slide 41

Slide 41

You can read all about these in the 30DaysOfPWA series.

Slide 42

Slide 42

So that gets us an install prompt, but what if we wanna take things further?

Slide 43

Slide 43

On Android, for instance, there’s an enhanced installation dialog that looks like a product page you’d find in an app store. We can add a few more entries to our Manifest to enable this we need to add some more members to the Manifest…

Slide 44

Slide 44

First up is the description

Slide 45

Slide 45

Then screenshots, which use ImageResource again

Slide 46

Slide 46

It’s really important to include labels for your screenshots, for accessibility

Slide 47

Slide 47

And if the screenshot is of a particular form factor or unique to a specific device or OS, indicate that using platform. You can specify key words like wide, narrow, windows, macOS, etc.

Slide 48

Slide 48

Slide 49

Slide 49

So we saw how this can improve the install dialog in the browser, but…

Slide 50

Slide 50

It can also help with your listings in traditional app stores too. Speaking of app stores, let’s talk…

Slide 51

Slide 51

…about some of the ways folks can discover your PWA

Slide 52

Slide 52

First o!, you can distribute your PWA through traditional app stores. Packaging your web app for these platforms is beyond the scope of this talk, but it’s something that can be done relatively easily…

Slide 53

Slide 53

Using an open source tool called PWA Builder. Being in app stores also opens up the possibility of promoting your app to new customers via crosspromotions. It also opens doors for discussing things like getting your app pre-installed on specific devices or operating systems.

Slide 54

Slide 54

Once you’re in stores, your app may begin to appear in search results as available software too.

Slide 55

Slide 55

There are also a host of indie app catalogs out there, only a few of which I’ve highlighted here: findPWA, PWAStore, PWAList and AppScope

Slide 56

Slide 56

So, to summarize, taken all together, there are a host of discovery and distribution opportunities for PWAs that increase your app’s visibility.

Slide 57

Slide 57

The final area I want to touch on with respect to PWAs is integration opportunities.

Slide 58

Slide 58

As this screenshot from WhatWebCanDo.today shows, there are a ton of APIs and device capabilities available to the web platform now. I strongly recommend taking a look at that site to get a sense of what your browser is capable of. You’ll probably find a few surprises in there.

Slide 59

Slide 59

Integration is one area where we can really improve our users’ experiences in our apps. And as with everything else I’ve discussed today, these improvements can be made as progressive enhancements to your site. It will continue to be usable without them, but really comes into its own when the opportunity to more deeply integrate with the OS presents itself.

Slide 60

Slide 60

I’m going to talk about six broad categories of integration today, but there are a ton more.

Slide 61

Slide 61

We’ll start with sharing. First o!, what do I mean by sharing?

Slide 62

Slide 62

Sharing, in this context, is when one app can hand off data to the OS for use in another app (share from) and the other app is set up to receive shared data (share to).

Slide 63

Slide 63

On the web, these map to the navigator.share() API for sharing from your site And a share_target definition in the Manifest to receive shares

Slide 64

Slide 64

Sharing from is (currently) only available in JavaScript.

Slide 65

Slide 65

Like most modern APIs, the share API is asynchronous, meaning it’s non-blocking and Promise-based. If you’d like to learn more about Promises, I highly recommend Nicholas Zakas’ book Understanding JavaScript Promises. If we want to make a wrapper function for sharing, we need to make that function asynchronous, which is what you see here.

Slide 66

Slide 66

Inside that async function, we define our share data, which is a JavaScript object. In the example I’m showing, the data being shared includes a title, some text, and a url.

Slide 67

Slide 67

There are a bunch of ways to trigger the share, but the using try/catch is a pretty easy way to do so in a way that isn’t going to cause issues for your users. Here, in the try block, we await the Promise returned by calling navigator.share with the share data.

Slide 68

Slide 68

And here you can see the native share dialog invoked in macOS by this method.

Slide 69

Slide 69

84% of global browser usage

Slide 70

Slide 70

You can also test to see whether share is available and even whether your data package can be shared.

Slide 71

Slide 71

This is especially useful if you are sharing files because it will tell you whether the file format is supported.

Slide 72

Slide 72

File sharing isn’t everywhere yet, but it is available in a lot of places. Now that we’ve talked about sharing from our app, let’s talk about sharing to our app. 80% of browsers

Slide 73

Slide 73

What we want to do is show up in the share picker (this one is from Android). We do that using the share_target member of the Manifest.

Slide 74

Slide 74

The share target definition looks pretty complex, but it’s not overly dense, especially if you think of it as analogous to setting up an HTML form

Slide 75

Slide 75

The first three properties map directly to the form element: action, method, and encoding. These set up the means by which the info is transmitted to your app. Use GET for recipient pages that have some interim step before submission, POST for ones that don’t require user interaction.

Slide 76

Slide 76

Then there are the parameters. Think of these as your form field names. This is where you define the parameters that get passed on the query string or in the POST.

Slide 77

Slide 77

On the left are the ones the share operation defines and the ones on the right are the parameter names your app expects. This object maps one to the other.

Slide 78

Slide 78

I mentioned you could share files… you can also accept them. This example is from Twitter and it

Slide 79

Slide 79

Enables the Twitter PWA to, among other things, integrate into the File Explorer in Windows

Slide 80

Slide 80

~68% of browsers, globally

Slide 81

Slide 81

The next integration I want to touch on is advertising your app as a file handler. That means your app can appear in the “Open with” context menu.

Slide 82

Slide 82

As with share target, we define our file handlers in the Manifest

Slide 83

Slide 83

The file_handlers member is an array of one or more file association objects.

Slide 84

Slide 84

In this object, you define the name of the file type (here it’s Scalable…)

Slide 85

Slide 85

The mime and extension associated with it (in this case image/svg…)

Slide 86

Slide 86

The icon(s) the OS can use for this file type (which is an array of ImageResources)

Slide 87

Slide 87

And, finally, the URL ”action” (like with share_target) that is able to read and open this kind of file.

Slide 88

Slide 88

Thomas Steiner put together a demo app called Excalidraw that makes use of the File Handler feature. Here I have a Finder window with an excalidraw file in it. Right clicking that gives me an Open With menu item and Excalidraw is the top hit. Clicking to open the file launches the PWA, even if it’s not open, and opens my file. Now think about this for a second… this whole experience was made possible by about 15 short lines of JSON in the Manifest. How amazing is that?!

Slide 89

Slide 89

It’s worth noting that, on the action page, you’ll use JavaScript to access the file details. This shows how you can test for support for the feature by seeing if launchQueue exists on the window object and if “files” is a part of the LaunchParams prototype.

Slide 90

Slide 90

As you could see from my screenshots, this is already working in Canary versions of Chromium browsers. It will be available in their stable channels soon.

Slide 91

Slide 91

Now earlier I mentioned that VSCode was available as a PWA. You can check it out for yourself by visiting vscode.dev or (if you want the beta version) insiders.vscode.dev. In order to be a useful text editor, VSCode needs access to your local file system. It accomplishes this through the File System Access API. With this API, which requires user permission for obvious reasons, a website can interact with individual files or even whole directories on the user’s local machine.

Slide 92

Slide 92

The File System Access API is fairly similar to similar APIs in other languages. It provides a host of utilities for working with files and directories, including…

Slide 93

Slide 93

As you can probably imagine, misusing this API can have catastrophic effects. Please exercise caution.

Slide 94

Slide 94

I don’t want to spend a ton of time talking about this API, but I do want to show you a brief example to give you a sense of how it works. Imagine this code running in a web page with a button—which we’ll wire up to open the file picker—and a textarea that we’ll populate with the contents of the chosen file. We’ll also assume it’s a text file (we can specify the kinds of files we accept too, but I want to keep this example simple).

Slide 95

Slide 95

We can start by checking to see if the API is supported by testing for one of the methods: showOpenFilePicker, which is available on the Window object

Slide 96

Slide 96

Once we know that is available, we can create a variable for storing our file handle… more on that in a second and capturing references to the button and the textarea

Slide 97

Slide 97

Then we can set up the event listener for a button click. As this API is asynchronous, we need to make sure the callback is an async function.

Slide 98

Slide 98

Within that function, we call showOpenFilePicker and wait for a response, which will include a reference to the file called a file handle. Interestingly enough, you can store this file handle (and directory handles too) for later using IndexedDB, potentially allowing you to skip this step in the future.

Slide 99

Slide 99

Using that file handle, we can request the file itself and then read its contents, both of which are also async operations

Slide 100

Slide 100

Finally, we can take those contents and drop them into the textarea.

Slide 101

Slide 101

Circling back to this line, I want to note that the showOpenFilePicker() method can take an options object that defines things like the file types you can accept, common file system locations you like to start the user in (like Downloads or Pictures). You can use this same options object to define things like a suggested name for a file the user is saving. All of this is beyond the scope of what I have time to talk about today though. Suffice to say, the File System Access API is incredibly powerful and it eliminates one of the final major barriers to building software on the web.

Slide 102

Slide 102

This is a very new API, but it has been available in Chromium browsers since version 86, meaning roughly ~30% of browsers have this feature already.

Slide 103

Slide 103

The next integration I want to talk about is protocol handling. But before we get there, some of you may be wondering… what’s a protocol?

Slide 104

Slide 104

Technically these are schemes, but here are some examples of protocols you might be familiar with (and some you might not be). What a protocol handler lets you do is declare your app as being able to resolve URLs with the identified protocol(s).

Slide 105

Slide 105

As with file handlers and share targets, we enumerate protocol handlers in the Manifest

Slide 106

Slide 106

Protocol handlers are very straightforward. Each protocol handler is an object with two members: a protocol to handle and the URL to route it to. This example comes from the Outlook PWA.