Disclaimer: I’m not affiliated with Google Play, Chrome or any other company mentioned. This is not an official statement; usage of the logo and name is just for illustration.

⚠️ Updated version of this article ⏩ firt.dev

Chrome 72 for Android shipped the long-awaited Trusted Web Activity feature, which means we can now distribute PWAs in the Google Play Store! I played with the feature for a while, digging into the APIs and here you have a summary of what’s going on, what to expect and how to use it today.

This article is an introduction and I’m working in a tutorial series, so if you are interested in that, follow me on Twitter or subscribe to my newsletter.

PWAs can now play in the Store

Chrome 72 for Android is now shipping from the Play Store to all users and this version included Trusted Web Activity (TWA), that in a nutshell is a way to open Chrome in standalone mode (without any toolbar or Chrome UI) within the scope of our own native Android package.

Let me start saying that the publishing process is not straightforward as it should be (such as “enter your URL” in the Play Console and it’s done.) It’s also not a way to use the currently available WebAPK and publish it in the store. It’s a Java API that communicates through services with Chrome and seem to be in the early stages, so there is a lot of manual work to do yet today.

I see this as the first step and I’m sure better tooling will appear soon (maybe from the community) using these now available APIs to create a one-click to store mechanism. But with the current API, it’s possible to ship a PWA’s launcher. I will share my experience in this article for everyone interested.

For some reason, the feature doesn’t appear as shipped yet in Chrome Platform Status and there is still no updated documentation available more than the one published 15 months ago.

*UPDATE 2/5: The Chromium Blog posted a short post confirming TWAs and with some technical confirmations of this article.

*UPDATE 2/1: Paul Kinlan, Lead of Chrome DevRel @ Google confirmed TWA was shipped and docs are just delayed; the reason for the delayed official notification and docs is that the go/no-go for the feature on Chrome 72 was a last-minute decision.

Here you can watch a video of Pete Le Page explaining TWAs a year ago.

Why publishing a PWA in the store?

Is the Web Platform, Max! Why do we want to use the Store?

Well, it’s a long discussion, but I’ve been doing consulting with small and big companies for years, and when talking about PWA investment, the store is a recurrent demand. “My users will look for my app in the Store”, “I already have a native app that I want to stop doing, but I don’t want to lose my users”, “I need to access a native API”, or “I want to monetize my PWA.”

From now, publishing your PWA in the Store -as well as from the browser- will be possible. Of course, it’s an opt-in operation. The Play Store is not emulating the Microsoft Store: your PWA won’t be listed in the store if you don’t compile your own APK, and publish it.

Advantages over standard PWA installation

Despite the new distribution mechanism and people finding your app within the store (and even in Google’s search under “Apps”), there are some things we may be able to do with a PWA in the Store, such as:

Offer a Home Screen Widget

Offer App Shortcuts (when you long press the app’s icon) and other deep integrations with the OS

Offer a wear OS companion app or Android Auto extension

Be re-installed after a hard reset or a backup restore on a new phone

Have background services accessing native features (communication with the PWA is still limited — more on this later)

Monetize the App — limited today, more on this later

Have some native screens mixed with PWA content

Distribute more than one PWA icon in the Launcher and/or home screen pointing to different URLs (within the same host)

Better internationalization support

What’s new here?

There were already some PWAs in the Store, but now they will be much easier to produce and publish

In Google Play Store there are already some PWAs published such as Google Maps Go, Instagram Lite or Twitter Lite. The first one is using some kind of a private pre-TWA version, and the last two are using a WebView that while not ideal, it was the only way to do something like this before TWAs. These apps are adding a lot of native code for some things such as notifications. We want to publish PWAs as web developers, we don’t want to write a lot of Java code.

TWA is a special mode on Chrome Custom Tabs, a solution available to native Android apps from Chrome 45 to open an In-App browser.

Is TWA a Hybrid framework, similar to Cordova?

No. With Cordova or other hybrid solutions, you are typically shipping your web resources (HTML, JS, CSS, etc.) within your APK package. Also, the engine is different and isolated from the users’ browser, so no session or cache sharing.

This is how a PWA looks like with Trusted Web Activity (Starbucks actually didn’t have one yet). I don’t see the theme-color implemented yet

With Trusted Web Activity you don’t need to package any resource file from your PWA (only native components, in case you want them); all your resources will be downloaded and updated on the fly from your Service Worker. Your PWA will still be rendered with the installed Chrome version, sharing all storage, cache, and sessions with the browser. Therefore, if your user has a session on your website opened when the user installs the app from the Play Store, she will still be logged in. The user is just installing a shortcut to Chrome using a special mode.

Play Store approval requirements (*UPDATE 2/5)

The URL you use for the Trusted Web Activity must comply with:

Passing the PWA Criteria (typically today: HTTPS, a Service Worker with a fetch event handler, and a Web App Manifest with a 512px icon, a background_color, and basic other properties set.)

Performance Score with a minimum of 80/100, tested with Lighthouse (available in Chrome Dev Tools, or as an NPM CLI.)

All current Google Play Store rules.

As per the Chromium Blog post, published a few days after this article was first published, “Apps which fail to meet TWA quality requirements or Play store policy may be denied entry or delisted.”

I’m not completely sure yet if these requirements will be checked by the Play Store QA team when publishing the app or not.

Security Model of PWAs in the Google Play Store (*UPDATE 2/4)

Your PWA will run under the browser’s security model, not under your native app one — unless you add native code as well to your APK. Because the Activity will be managed by Chrome, the user should we aware that even if she has just installed the app, she might have session data, local storages and permissions already set for that host. That’s why when the user opens the shortcut icon for the first time she will see a “Running in Chrome” mini infobar, as stated by Robin Bakker:

Also, when the user uninstalls the app from the Application’s Manager, we will get a warning stating that app’s state and data is still available while in Chrome, so she should clear cache as well if needed. That includes, for example, the Web Push permission, so even if the user uninstalls the app, the app can still send push notification messages. The message was spotted by Henry Lim:

These dialogs and the reason for them were confirmed by Paul Kinlan, from the Chrome team.

Development options

For creating an Android Package using TWA we will first need Android Studio. Right now, the options available are kind of experimental and documented only with some open source examples in the Chrome GitHub repository.

We can develop apps with TWA:

Using a high-level Java Support Library provided by the Chrome team: in this case, you don’t need to write any Java or Kotlin native code; you create an Android Studio project (or clone the example), set up some metadata in AndroidManifest.xml from your Web App Manifest and you are done.

The framework will provide the connection with TWA and optional abilities to create a Settings entry in the Android device and to make Web Push notifications available. The framework is currently available as a support library in a temporary Jitpack repository. I guess that’s not going to be the final location for this library in the future.

provided by the Chrome team: in this case, you don’t need to write any Java or Kotlin native code; you create an Android Studio project (or clone the example), set up some metadata in AndroidManifest.xml from your Web App Manifest and you are done. The framework will provide the connection with TWA and optional abilities to create a Settings entry in the Android device and to make Web Push notifications available. The framework is currently available as a support library in a temporary Jitpack repository. I guess that’s not going to be the final location for this library in the future. Using the Trusted Web Activity manually. If you have experience developing Android apps with Java or Kotlin, you can just plug your PWA manually into your app. That means you can have some native activities and at one point you can open the Trusted Web Activity with your PWA. In this case, I suggest analyzing the Support Library to understand how to connect to Chrome from your project.

*UPDATE 2/5: There is now available documentation in the Android website regarding TrustedWebUtils, the TWA Helper.

Spreading the Manifest

A PWA in the App Store won’t use the Web App Manifest for defining how your app runs; we will need to copy some of those values manually. Icons will be taken from the “res” folder like any other native Android App, orientation lock should be defined in AndroidManifest’s activity entry, etc.

If you are using the Java Support library, a splash screen might be created for you automatically but other properties from the manifest won’t be used. In fact, in my testing, I couldn’t see the theme-color being applied yet when my PWA is on the screen.

URL Validation

TWAs will work only if we digital handshake our domain with our app. This is a mechanism known as Digital Assets Links. That will make a trusted relationship between your host and your APK, proving that you are the owner of the PWA and that you won’t be publishing PWAs in the Play Store that you don’t own. It also makes a digital link between your website and your native app that in theory can let them share private data (but doesn’t seem to be possible with today’s TWA API).

With Digital Asset Links you must serve under your PWA’s domain a file in the URL <your-domain>/.well-known/assetlinks.json. That JSON file will contain information about your Android package (such as the package ID) and a hash of your app’s certificate that you can get running a command in the terminal. Your Android package will have a counterpart setting the host URL. There is an online validator tool available to check if everything is fine.

If you don’t do the handshake, TWAs won’t be activated and your app will just use normal Chrome Custom Tabs with a Chrome user interface similar to when your PWA has display: minimal-ui. I’m not completely sure but I guess the Play Store might reject apps that are just pointing to normal Custom Tabs and not validated TWAs. I’m not completely sure yet when Chrome is doing the Digital Asset Link check; if it’s being done on every access to the app before opening it as a TWA it might be a performance problem. I guess a cache will be possible, and also the Play Store might check this before accepting the app. We’ll see if future documentation gives us clarity on this matter.

There is a (not so simple) mechanism to bypass the Digital Asset Link certification process for development purposes, explained below in this article.

Publishing your App

To publish your PWA shortcut using TWAs you will need to follow all the Google Play Store rules. Check the Developer Policy Center for more information. You will also need a Developer Publisher account paying a one-time fee of USD $25 and create metadata, screenshots and marketing material for your app.

To publish in the Google Play Store you must accept developer agreement and pay a one time USD $25 registration fee

Deployment

When you are done creating your APK (Android Native Package) from Android Studio and you already have a Developer Console account, you must create a production APK and sign it with a self-created key that you create within Android Studio tools. You might also want to check App Signing by Google Play to simplify the process in the future.

The Google Play Console

There are no special rules or processes to upload these apps to the Play Store, but the Revision team might detect that you are using TWAs and will do a check that 1) Digital Assets Link is enabled and validated, and 2) the URL is passing the PWA criteria (mostly for a Service Worker fetch event handler).

Filling a lot of metadata and graphic assets will be mandatory for publishing your PWA in the Google Play Store

Updating your App

You don’t need to upload your app again if you change web content — unless you change the app completely as per the store rules. You will continue updating your app through Service Workers and new deploys to your server. You will have to create and upload another APK only if you want to change metadata, native code, or icons.

Limitations

I’m seeing a list of possible limitations regarding the platform today, but it’s just a start and I hope we will see improvements over new versions.

PWAs in Subfolders

If you publish your PWA in a subfolder of a host, it seems to be a couple of issues here.

Digital Asset Link connects the whole domain, not just a folder

The current Support Library seems to handle as an Intent (Link Capturing) the whole host, even if your PWA is in a subfolder

No internal apps

While this is a restriction of the Play Store itself (you can’t publish intranet apps, or apps that are just for you or your company), you might want to use TWAs and create APKs that you will deploy outside of the store.

That won’t be possible as Digital Asset Link works only with public URLs because Chrome needs to verify we own the domain, and that’s is not possible with internal URLs yet.

First Load experience

The first time you open the recently installed app, you don’t have any actual files from your app (Service Worker wasn’t registered yet — unless, the user visited the PWA before), so if you are offline you will end up with a blank white screen. I think trying to warm up Chrome after installation somehow will be useful in future versions. If you are using TWA APIs instead of the Support Library you might be able to detect this situation and inform the user properly using native APIs.

Calling native code

There is already a bi-directional channel to communicate the TWA Server (Chrome) and the TWA Client (our APK). That channel is currently being used to send Push Messages and show them as being part of our native app and not Chrome, but nothing else yet.

There is a potential here to bridge native code and JavaScript code without too much effort and let our PWAs access native code, similar to what happens when we publish an APPX with a PWA for Microsoft Store.

I’m seeing a future version with a way to register Java/Kotlin classes to the TWA Client so we can actually call them with a JavaScript API when our PWA is rendered in TWA mode.

Today, the only way to execute native code is to use Intents to open other native Activities and back opening TWA sending and receiving arguments through URIs parameters.

Also, you can create some kind of web server, or WebSocket server in native code running on a Service and let the PWA talk to it, but it’s kind of weird, complicated and maybe battery consuming. But there is a whole new world that might open now. Let’s see what the community creates!

Monetization with the Play Store

If your app is not free to download, there will be no easy way to validate that the user has actually paid for it (at the end, your content is just a URL); also if you have digital assets or subscriptions that you’d like to sell using the Play Store wallet, it will be a challenge to make it work without having an actual bridge to native code.

Debugging

I’m not sure if it’s a bug or something on my development environment but remote debugging the Service Worker from the TWA is not working. I can inspect the window’s context, but not the Service Worker.

Other engines

TWAs work only with Chrome today, but the API might be also cloned by other browsers, such as Samsung Internet, Edge or Firefox in the future.

*UPDATE 2/4: The TWA works over the Android Custom Tab protocol that other browsers are currently implementing, so if the user doesn’t have Chrome or has changed the default browser, another browser might take precedence and open the TWA with the PWA content. More testing is needed to understand how it works.

What happens if the user has an older version of Chrome and installs the app from the Play Store? In this case, your PWA will appear as a Chrome Custom Tab, not in a completely standalone mode.

Google Maps Go in the Play Store was already using something similar to TWA specifying Chrome as a requirement

What happens if the user doesn’t have Chrome at all? As of today using the Support Library, your app won’t work at all (unless there is another browser listening to the Custom Tabs protocol). If you are using the TWA API on your own Java/Kotlin code you might be able to detect browser availability and load an alternate solution, such as a WebView or opening the browser.

While it’s not so common to see Android devices without Chrome, there are some devices without it by default, including new devices in Europe shipping without Chrome by default.

Other platforms

PWAs won’t work on wear OS (watches), but I’m not completely sure what happens on other Android platforms. I will guess a probably not yet, but I’ll test it for a next article. I’m talking about Android TV or even Chromebooks with Play Store. In the meantime, if you didn’t test it, it might be a good idea to disable those platforms in your store listing.

Conflicting with WebAPK

If you already installed the PWA from Chrome, you already have installed an APK for that URL signed by the Play Store, but that won’t stop the store to list your App and let the user also install it. The same on the other way: having the app installed from the store won’t stop Chrome to offer the user to “Add” it from the browser. I think this might be avoided in the future if the WebAPK also has a Digital Asset Link somehow, or if we can match WebAPK’s app’s id, but I don’t see this happening soon. We’ll see.

Two Starbucks Apps running at the same time: WebAPK and our own APK

You can stop Chrome to offer WebAPK and offer your store listing app instead by using the related_applications attribute and prefer_related_applications Web App Manifest’s attributes. Get Installed Related Apps API might help in the future with this conflict.

Detecting a Trusted Web Activity (*UPDATE 2/7)

If for some reason, you want to detect if the user is on a TWA there are two ways:

Add an argument to the start URL the TWA will load

Use the Referrer: On the first navigation page load in a TWA, document.referrer will be set as android-app://<package-name>; the same value will also be set as well in the HTTP Header Referer (misspelled in the HTTP spec)

Creating your first PWApk

Yes, I know, I’ve just invented the PWApk word, but it doesn’t sound too bad, right?

This article is an introduction and I’m working in a tutorial series, so if you are interested in that, follow me on Twitter or subscribe to my newsletter.

A few years ago I did a video course on Native Web Apps for Android, that while not exactly what you need to do for PWAs, it will help web developers understand the Android ecosystem.

The simplest way to make a TWA-based APK is to clone the SVGOMG repository sample from ChromeLabs GitHub repository

You can clone the git repository or start a new project.

In our case, we will start a new project just to understand and explain what’s going on with the solution.

Create a new Project in Android Studio and select “No Activity” as we will use just a Trusted Web Activity provided by the Support Library.

We start with an empty project

Fill the details, picking a name (we will override this later), and a Package name. The package name is important as it will be the ID of our app in the Android OS and also within the store. I recommend using your PWA’s host in reserve order and an optional name after, such as: com.mypwa.calculator if your PWA is https://mypwa.com/calculator

API 19 (Android 4.4) as a base looks good, as it seems that is going to be the minimum version required for Chrome soon. Some things on TWA will work only from API 23 (Android 6.0) but the Support Library will take care of that.

*UPDATE 2/5: Chrome formally announced that TWAs work only from Android 4.4 KitKat. There is around 5% of active Android users that won’t have the feature and they will fall back to a Custom Tab experience with a URL bar.

Picking the minimum API level will limit the devices that will see our PWA in the Google Play Store

Adding Dependency

The next step is to add the TWA Support Library as a dependency, so we will go and open two files with the name build.gradle

There are two gradle configuration files, one for the project and one for the Android app

Starting with the Project build.gradle, we will add under allprojects > repositories, the following line:

maven { url "https://jitpack.io" }

Next step, we open the Module build.gradle and we add under dependencies:

implementation 'com.github.GoogleChrome:custom-tabs-client:e446d08014'

Configuring the TWA

The next step is to stay under the Module’s build.gradle file and setup the PWA settings for the Trusted Web Activity, under defaultConfig we will add:

manifestPlaceholders = [

hostName: "app.starbucks.com",

defaultUrl: "https://app.starbucks.com",

launcherName: "Starbucks",

assetStatements: '[{ "relation": ["delegate_permission/common.handle_all_urls"], ' +

'"target": {"namespace": "web", "site": "https://app.starbucks.com"}}]'

]

In this case, I’ll use the Starbucks PWA as an example. The assetStatements key is the one that will need information from the Digital Asset Link process. We’ll skip that part for development purposes. The property with the name launcherName should match short_name in the Web App Manifest.

Configuring the Manifest

Android apps have their own manifest and they won’t use our Web App Manifest, that file is under app > manifests in your Android project explorer as is called AndroidManifest.xml. You will find a self-closed Application XML element there.

The default Android Manifest file we have now

There, we can change the value of android:label with ${launcherName} to use the launcherName we set before in the metadata, so we will have one single source of truth for the app’s name.

The next step is to setup this file, starting with opening the <application> tag so we can add some children inside that will look like the following code:

<meta-data

android:name="asset_statements"

android:value="${assetStatements}" />



<activity android:name="android.support.customtabs.trusted.LauncherActivity"

android:label="${launcherName}">

<meta-data android:name="android.support.customtabs.trusted.DEFAULT_URL"

android:value="${defaultUrl}" />



<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>



<intent-filter>

<action android:name="android.intent.action.VIEW"/>

<category android:name="android.intent.category.DEFAULT" />

<category android:name="android.intent.category.BROWSABLE"/>

<data android:scheme="https"

android:host="${hostName}"/>

</intent-filter>

</activity>

The code below will set up the TWA using the Support Library, the Intent Filter so your app will capture links to your PWA and the Digital Asset Link. I will skip the details on what’s going on there from an Android app’s point of view.

It’s time to Sync

At this point, you will have to make Android Studio take all your changes by clicking on “Sync Now.” If everything is correct, you won’t get any errors at this point.

Icon replacement

Now our app uses just a default android icon, we should replace all the files available in app>res>mipmap in different subfolders for different pixel densities. There are two versions, square and rounded icons. Rounded icons are new in Android 7.1 and if you want to skip them remove the android:roundIcon reference in the AndroidManifest.xml.

We need to manually take icons from our Manifest and copy them in the mipmap subfolders with the right name

Theme adjustment

Finally, we should open app/res/values/styles.xml and make some changes to the theme so it will look like a PWA:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

<item name="colorPrimary">@color/colorPrimary</item>

<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

<item name="colorAccent">@color/colorAccent</item>

<item name="android:windowNoTitle">true</item>

<item name="android:backgroundDimEnabled">false</item>

</style>

You can change the colors of the app from app/res/values/colors.xml but I didn’t see them yet in the TWA.

If you are currently restricting orientation in your manifest, you might also want to add that restriction in the <activity> element in the AndroidManifest.xml.

Ready to test?

We are ready to test our PWApk, but before that, we need to set up development TWA mode for Chrome in our Android testing device or emulator (with Chrome 72+).

Setting up Chrome

Check that you have Chrome stable version 72 and open chrome://flags. Search for “Enable command line on non-rooted devices,” and enable the flag. You will have to restart Chrome.

Next, we need to set up Chrome to bypass Digital Asset Link for the host we want to test, in our example: app.starbucks.com. (I’m sorry Starbucks, but we love you, you know that LOL)

To change command line arguments for Chrome on Android we need to write a text file in the Android’s file system. The simplest way to do it is through adb (android debug bridge) that has to be in the path (google that if it’s not there) and execute:

adb shell cat /data/local/tmp/chrome-command-line _ - disable-digital-asset-link-verification-for-url="https://app.starbucks.com"

There is a simple bash script available in the Chrome’s sample to use if you want to.

We need to stop Chrome to take our new dev setting

And now, we need to restart Chrome. But not just killing the app from the multitask window. Go the Settings / Apps / Chrome and Terminate the whole process (Force Stop). I had to do this a couple of times before having it ready.

The warning stating that the flag is enabled

If it’s done, then every time you open chrome you will get a warning now about that flag we enabled and if you run your app from Android Studio you will finally get your PWA up and running in standalone more under your APK’s icon and name.

Conclusion

The idea that we can now publish PWAs in the Google Play Store is really a game changer. It seems we are in the early stages of the API right now and we definitely need a higher level solution for this. I really want to see a tool to enter our PWA’s URL and get an APK from it. It won’t be simply because of the Digital Asset Link verification system. Only the Play Store can do that just by using the same WebAPK they are generating.

I was questioning if Google was approving or not TWAs, so they are removing the suspicious at least of the TWA part of the article.

Let’s take Chrome 72 TWA as the first step of a long journey!

I want to thank Paul Kinlan for his support regarding TWAs, my questions and because I’ve been pinging him almost every month about this :P

Maximiliano Firtman is an independent mobile + web developer, trainer, speaker, and author. He has authored many books, including High-Performance Mobile Web published by O’Reilly Media. He is a frequent speaker at conferences worldwide and he has been widely recognized for his work in the mobile-web community. He teaches mobile, JavaScript, PWA and web performance trainings for top companies around the world. He is an independent consultant and he has delivered several PWAs workshops and courses at many companies and at online publishers, such as Linked Learning/Lynda and Safari. Twitter: @firt