Publishing a Progressive Web App (PWA) on the PlayStore – What works and what doesn’t (in 2022)
Tags: pwa, twa, playstore
Published: 2021-04-12 19:19:14

I’ve been working on a [micro journaling app](https://journalisticapp.com) (PWA) over the last year, thinking that with a little notching users would just get used to installing apps directly from their browser, but errrrr…

Turns out they don’t. Not yet.

So I was stoked when I realized that with Google’s [Trusted Web Activities (TWA)](https://developer.chrome.com/docs/android/trusted-web-activity/overview/) you can just publish a PWA on the PlayStore.

The result is actually pretty great, you can check it out here: [Journalistic app on Google Play](https://play.google.com/store/apps/details?id=com.journalisticapp.twa). It quacks like a duck, mostly…

![Journalistic TWA screenshots: text input, lists, charts, menu](https://cdn-images-1.medium.com/max/2800/1*HJfQOtSNQ6QDaGhs8eUAkg.png)*Journalistic TWA screenshots: text input, lists, charts, menu*

The stack behind Journalistic is [Django](https://www.djangoproject.com/) and [Nuxt](https://nuxtjs.org/). The [nuxt/pwa](https://pwa.nuxtjs.org/) module takes care of everything PWA-related on the app side: manifest, service worker, etc. Easy peasy.

Generating the app bundle is also more or less straight forward if you use [bubblewrap](https://github.com/GoogleChromeLabs/bubblewrap). There are some pitfalls with the app signing shenanigans but after a few hours I managed to have a working bundle and it went straight into the PlayStore review process.

Turns out that Saturday was well spent, more than 80% of new signups come through the PlayStore now, without any buck or minute spent on promoting the listing (except the 25€ one-time fee for the Google developer account).

There is quite some hacking required to make the web app truly behave like a native app, especially on the CSS side, but it can be done for the better part. Since there are already a lot of posts about this topic I won’t get into details here. Let’s look at what doesn’t work well (yet).

--- 

## Bummers
 
### Navigation bar color

You can customize the navigation bar color in the app manifest, but only static. For a lot of apps that might be enough, but if you have a dark mode for instance and want the user to be able to turn it on/off manually via a setting in the app you have a problem. I’ve [raised the issue with bubblewrap developers](https://github.com/GoogleChromeLabs/bubblewrap/issues/429) and the problem clearly is a lack of support on the Chrome side.

I’ve resolved this to some extend by detecting and using the system theme as default (you can specify a color for light- and for dark mode). This works well until the user decides to choose a theme manually. If the device is in dark mode and the selected theme is “light”, things are not so bad, you get a black bar at the bottom, not ideal, but not too bad either. But if the device is in light mode and the user chooses a dark theme they get a dark app with a white bar on the bottom and that is really not cool.

### Status bar color

You can also customize the status bar color, even dynamically, until again, dark mode. When the device is in dark mode it will always use a weird dark gray as background for the status bar, which raises the question: “WHY?!!”. Same problem, the limitation is on Chrome side.

### Performance

Javascript is single-threaded. You can achieve multi-threading with web workers, but for most use cases they don’t bring any benefit and for others implementation is quite tricky. As a result, a TWA will feel clunky and less responsive when compared to a native app. Especially scroll animations, chart rendering, aso. can introduce quite some lag and compromise UX.

### Text input

Text input is generally a big problem on mobile devices and it gets even worse if you’re in a browser. While you have some control over input elements in native apps, dealing with <textarea/> is simply a nightmare. On top of that you have no control or even knowledge over what the soft touch keyboard is doing. Eventually you’ll have to adjust (and probably compromise) your design to cope with the weirdness.

Ah right, if you have a nav bar at the bottom, you’re f***ed anyway.

![Journalistic UX example for text input with controls](https://cdn-images-1.medium.com/max/3114/1*vYi03naPkCqHhOUZSkQjBw.png)*Journalistic UX example for text input with controls*

### Monetization

[Google is currently running trials in Chrome (88 and 89)](https://developer.chrome.com/docs/android/trusted-web-activity/play-billing/) to integrate Play Billing via the [Payment Request](https://developers.google.com/web/fundamentals/payments) and [Digital Goods](https://github.com/WICG/digital-goods/blob/master/explainer.md) APIs. This is definitely a big step in the right direction, but it will probably still take a bit until this is fully supported.

If you want to monetize your app, this might be the dealbreaker and even if Play Billing is fully available eventually you will still have to shoulder the burden of integrating two different payment systems, the Google one and the one you use for the web app ([Stripe](https://stripe.com/) or whatever).

### Data storage

You can of course use local storage, just like in the browser, but if the user erases their Chrome storage it will also wipe out your app’s data, which is something most users would certainly not expect. So far I haven’t found a way around this, but tbh also haven’t been looking a lot.

### Annoying “Running in Chrome” banner

When opening the app for the first time the TWA will display a banner notifying the user in which browser it is running. This is in my opinion completely unnecessary and annoying. It just confuses users and gives the impression of a “fake” app. A better alternative would be to only display the banner if the app was installed with a browser that is not the default.

### Device APIs

Chrome already supports a lot of device APIs (way more than Safari for instance), but some important ones are still missing and some have really bad UX. If you want to access Bluetooth for instance via BLE, Chrome will drop an ugly browser menu in the pairing process.

--- 

## All in all

All in all, depending on your use case you can already get quite close to a native experience. Most of the outstanding issues are annoying but minor and could be (and hopefully will be) easily resolved on the Chrome side. Sadly progress is pretty slow here.

Over the long run I see the above mentioned performance issues as the biggest limitation for PWAs compared to their native counterparts and the question is whether advances in web tech and on the framework side will be able to close the gap.

--- 

## Daydreaming

### Chrome

I hope that in the future Chrome will support dynamic theming for the status- as well as for the navigation bar via meta tags (text- and background color) and get rid of the weird behavior in dark mode.

Removing the “Running in Chrome” banner or introducing a more user-friendly UX for it would also be amazing.

I’m not sure if that would even be possible, but giving TWAs their own local storage and cookies, independent from Chrome, would be totally sick.

### Payments

Can there not be a single API that you can integrate and it figures out automatically who gets their cut when someone purchases something? 💭