World Wide Web
The web, as a platform, is open and free. Unlike native app markets, we don’t have to wait for software to be approved by any third-party. It works across any device or operating system that has a web browser. (Which is why standards across browsers is so important). But, until recently web-apps faced limitations. Not having full access to a device’s hardware and operating system was an issue – but that’s being fixed as more native APIs are being added to modern web browsers.
A disadvantage of having a web-only app was losing out on the discoverability that comes with having it listed in a searchable marketplace. Adding a web-app to your device home screen, from a web browser, is not intuitive to average users. Fortunately, the Google Play Market allows us to upload an app file that links to a progressive web app.
This involves a new protocol, Trusted Web Activities, as “a way to integrate your web-app content such as your PWA with your Android app“. The PWA leverages Digital Asset Links to “declare that it is associated with a specific Android app.”
Progressive web apps
I decided to try this out with one of my web-apps, BJJ Tracker. You can read about how I first built it on another blog post.
I had to make sure it qualified as a PWA. It needed offline support, as well as any other features that would make it feel like a native app. Google Chrome’s developer tools has a section called “Audits” that helped me identify such opportunities.
The first step was to create a “service worker” JavaScript file, and register it when BJJ Tracker loads.
if('serviceWorker' in navigator) { navigator.serviceWorker .register('/serviceWorker.js') .then(function() { console.log("Service Worker Registered"); }) .catch(error => { console.log(error.message) }) }
I added the above code to a shared file that loads on every page of my app. Below is an example service worker file. This file downloads any vital assets to a user’s device, and later loads them from the cache. Including a polyfill ensures that the cache methods exist (in case the browser does not support them natively). “We need to use the polyfill because the Cache API is not yet fully supported in all browsers.”
importScripts('/cache-polyfill.js'); self.addEventListener('install', function(e) { e.waitUntil( caches.open('bjjtracker').then(function(cache) { return cache.addAll([ '/', '/index', '/index?login', '/create-record?class', '/create-record', '/create-record?competition', '/view-record', '/view-month', '/privacy-policy', '/contact', '/view-more-data', '/account', '/css/bootstrap.min.css', '/css/bootstrap.min.css', '/css/bootstrap-theme.min.css', '/css/main.css', '/simpleMobileMenu/styles/jquery-simple-mobilemenu.css', 'https://use.fontawesome.com/releases/v5.3.1/css/all.css', 'https://fonts.googleapis.com/css?family=Roboto|Eczar&display=swap', 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js', ]); }).catch(error => { console.log(error.message) }) ); }); self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request).then(function(response) { return response || fetch(event.request); }).catch(error => { console.log(error.message) }) ); });
Read the documentation on Google’s developer portal.
Next, I created a “manifest” file. This file is written in JSON format. It helps describe how the web-app behaves once “installed”. It handles things such as app icon images and meta data.
{ "name": "BJJ Tracker", "lang": "en-US", "short_name": "BJJ Tracker", "start_url": "/", "display": "standalone", "background_color": "#2a4d69", "theme_color": "#2a4d69", "description": "Track Brazilian Jiu Jitsu progress and fitness goals.", "icons": [{ "src": "img/homescreen48.png", "sizes": "48x48", "type": "image/png" }, { "src": "img/homescreen72.png", "sizes": "72x72", "type": "image/png" }, { "src": "img/homescreen96.png", "sizes": "96x96", "type": "image/png" }, { "src": "img/homescreen144.png", "sizes": "144x144", "type": "image/png" }, { "src": "img/homescreen168.png", "sizes": "168x168", "type": "image/png" }, { "src": "img/homescreen192.png", "sizes": "192x192", "type": "image/png" }, { "src": "img/homescreen512.png", "sizes": "512x512", "type": "image/png" }] }
I created the image assets using open source software.
The manifest needs to be referenced by the app. I added a link tag to a shared <head> file. Additionally, I included a few other meta tags that let browsers know to treat this website as an app.
<link rel="manifest" href="/manifest.json"> <meta name="theme-color" content="#005b96"/> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="msapplication-starturl" content="/">
Android Studio
A signed app bundle is generated from Android Studio. I use a sample project from Google Chrome Labs as a template. We can clone that repository, and update the “/svgomg-twa/app/build.gradle” settings to point to our PWA.
The app’s icon files can be generated using an online tool. The downloadable bundle can be dropped into “/svgomg-twa/app/src/main/res/“.
When creating the app bundle (“Build > Generate Signed Bundle/APK”) we’ll need a signing key. I created a new one, and named the file mykeystore.keystore.
An “assetlinks.json” file needs to be uploaded to the web app’s host to satisfy the Digital Asset Links requirement. “The Digital Asset Links protocol and API enable an app or website to make public, verifiable statements about other apps or websites.” This confirms ownership of the PWA so that it can be linked to our app in the Play Store. To generate this file, first we’ll need to get the fingerprint from the signing key we used:
keytool -list -v -keystore mykeystore.keystore -alias mykeystore -storepass password-here -keypass password-here
That command shows us the certificate fingerprints. Copy the SHA256 value. It is used with Google’s Statement List Generator to create the contents of the assetlinks.json file. The statement file is then placed in a “.well-known” directory on the root of our PWA domain (eg. https://www.bjjtracker.com/.well-known/assetlinks.json)
Finally, I visited the Google Play Console. Besides uploading the .apk file, I also needed to include screenshots, featured image files, and complete a content rating survey – amongst other things. Since my app has been approved, you can now find it in the Google Play Market.
This app is a side project I use to toy with new web technologies. I’m trying to drive traffic to it so that I can experiment with optimizing conversions. I’m using it as a trial grounds for another software service called SplitWit. SplitWit is focused on optimizing conversions for the web, and helping digital marketers reach their goals. You can read about it on another post from this blog.