Archive for the 'PWAs' Category

February 6th, 2024
1:52 pm
Progressive Web Apps – Dynamic Cache Script Creation

Posted under Angular & Knowledge Base & PWAs & Web
Tags ,

My PWAs dynamically create and load a url cache in the service worker, when the installation event fires.
As this cache contains all the files in the dist/project-name directory tree (i.e. the entire deployment) for an angular app, I was keen to create the script containing the list of files to be cached dynamically as a post build step, so that the post build script could be generic and adaptive to the build contents.

This turned out to be pretty straightforward. The following points are noteworthy:

  1. I wanted to dynamically create a JS file which defined an array containing the urls of all the files to be cached. This script would then be included by the service worker to be used when it created and loaded the cache, during its install event.
  2. The above JS file was imported by the service worker script. I had some issues getting this to work via modules/module support, and noted from this post here that using the importScripts function instead (e.g. importScripts(‘pwa-cache-urls.js’);) gave better cross browser support/backwards compatibility. I therefore switched to using importScripts and it all worked fine.
  3. glob.sync was used to generate an array containing all the deployed files to be cached, excluding directories using {nodir: true}. This was then mapped/formatted as required into a string containing all the script for the file.
  4. fs.writeFileSync was used to create the script file from the above string.
  5. The project name and the generated script file name were passed as arguments to the post build file, in package.json. Whilst I did look for a dynamic way to derive/lookup the project name, an easy way to do this was not obvious and there was no loss of flexibility in just passing it in the postbuild script definition in package.json, along with the name of the dynamically created script file.

This all worked fine, and I was able to create a generic post build script that would be able to discover the files to be cached dynamically and create the script for them, for any angular project I was working with.

The prototype example code for the post build is below:

const glob= require(‘glob’);
const fs = require(‘fs’);

const args = process.argv;
validateArgs(args);
const projectName = args[2];
const cacheJSFileName = args[3];

const pathPrefix = ‘dist/’ + projectName + “/”;
const cacheJSFilePath = pathPrefix + cacheJSFileName;

fs.writeFileSync(cacheJSFilePath, buildCacheJSFile());

function validateArgs(args) {
  const argErrors = [
    “Project Name and Cache Filename were not passed to post-build.js”,
    “Cache Filename was not passed to post-build.js”
  ]
  if (args.length < 4) {
  throw new Error(argErrors[args.length – 2]);
  }
}

function buildCacheJSFile() {
  const mapPathDelimiters = (path) => path.replaceAll(“\\”, “/”);
  const mapPathPrefix = (path) => path.replace(pathPrefix, “”);

  const cacheJSFilePrefix = “const CACHED_URLS = [\r\n ‘./'”;
  const cacheJSFileSuffix = “\r\n]\r\n”;
  const formatCacheJSFileEntry = (file) => “,\r\n ‘” + file + “‘”;

  const cacheFiles = glob.sync(pathPrefix + “**”, {nodir: true});
  let mappedCacheFiles = cacheFiles.map((file) => mapPathPrefix(mapPathDelimiters(file)));
  let cacheJSFile = cacheJSFilePrefix;
  mappedCacheFiles.forEach( file => cacheJSFile += formatCacheJSFileEntry(file));
  cacheJSFile += cacheJSFileSuffix;
  return cacheJSFile;
}

Comments Off on Progressive Web Apps – Dynamic Cache Script Creation

February 2nd, 2024
4:18 pm
Progressive Web Apps – Technical Points/Issues Found

Posted under PWAs & Web
Tags , ,

Tutorials/examples used

  1. Simple tutorial here on free code camp
  2. Building Progressive Web Apps by Tal Ater. This includes a source example on github.

Issues/Points Found:

  1. My angular code has a number of cases where a query string is passed to a url. When caching a url or searching for one in the cache, it is important to remove all query strings and “#” fragment identifier. All my urls which are added to the cache are constants from a js/json file, and so no explicit removal is needed in the code. For searching, I use the {ignoreSearch: true} option on caches.match which strips all these on searching. Initially I appeared to have issues getting this working on all browsers, especially mobile, but in due course I established that there were other bugs at play, and in fact the above option works reliably across all browsers, including chrome and firefox on android.
  2. A valid manifest .json must be declared in index.html, and this must have at least one icon of at least 144×144 in size. This is a PWA rule.
  3. A promise was not being returned to the install event in free code camp example – this was due to braces being used in the install event code, but no explicit return was used to return the promise. The code did use a waitUntil in the install event, so clearly was expecting to wait for the cache additions to complete. The event waitUntil will therefore get an undefined return and not wait for the promise (i.e. the addAll to the cache) to complete. The PWAbook example above does do this correctly, and the code is very similar, but I copied and pasted code from each of the above in various examples and did not notice the missing return keyword in the free code camp example. This was subsequently remedied and all my code.
  4. Service worker are always installed immediately after registration see here:
    I have not always seen this. I have often failed to see the console logs for the install event and the cache add/creation on the web site devtools console, but I suspect this is due to an existing service worker being used rather than a new one. The service worker has its own dev tools page and having just loaded a PWA web site locally on a new unused port, I noticed that the install event console logging was visible both on the dev tools for the pwa site and for the newly created service worker. All of this happened before any attempt was made to install the PWA app via the browser.
    Service workers are reused unless terminated/removed, even when the web app is updated. However you can force a new service worker on update by visiting the Application/service workers menu option on the Application page of the dev tools, and ticking update on reload. I did not prove this/try out in detail, but I did leave it on normally when developing.
    This issue may also be due to timing issues due to the bug in 1/, the promise not being returned therefore the code does not wait for all the urls to be added to the cache.
  5. Service workers may be viewed via the following chrome urls:
    chrome://inspect/#service-workers
    chrome://serviceworker-internals/
  6. For android, there are no dev tools available, but you can enable usb debugging. Visit the developer options under settings, and then enable usb debugging. You can then plug the phone into a windows pc via usb, and visit chrome://inspect/#devices to list the available usb devices, and connect to them, which opens a dev tools window.
  7. Note that it takes up to a minute or so after the addAll is performed to the cache for it to appear as populated in the Applications tab of the chrome inspector. This was misleading at first as it looked as if it had failed/not been performed when it actually had.
  8. Initially I did often see errors loading certain urls into the cache. To log this, I switched to using a loop to iterate the cache urls array, and used sequential cache.add() calls rather than cache.addAll(). This allowed logging explicitly of the ones in error (cache.addAll did not give detailed errors about the urls that had failed). Cache.addAll() is better though as it performs the adds in parallel and so is faster, so having got the pwa working I have reverted to using cache.addAll. I suspect these errors were due to code issues, in particular the above timing issues due to the missing return keyword.

Having battled through all these issues, I was able to get reliable operation, both online and offline, and both on windows and Android, with both Chrome and Firefox.

Comments Off on Progressive Web Apps – Technical Points/Issues Found

February 2nd, 2024
11:14 am
Progressive Web Apps

Posted under Angular & Knowledge Base & PWAs & Web
Tags , , ,

I started looking into PWAs as an alternative to the Ionic framework which I have used previously to prototype an offline/online mobile application for my mobile places guide.

My main issue with frameworks such as Ionic was the need for multiple application codebases – one admin application for web based use online (which could also be used on mobile when online), and another application for offline or online use on mobile when actually out in the sticks and looking for a local place that best meets my already specified favourite criteria.

The immediate attraction of a PWA was the simplification due to a single codebase framework. As it is browser based, it can run with any web framework/app architecture and be usable online, and installable for offline use. Some of the main benefits are as follows:

  • Allows a single application framework such as Angular or React to be used for a web application which can be used online or installed for offline/online use just like a native app, i.e. can be run as an app from the home screen on a mobile. This allows a single application framework/codebase to be used for all scenarios. This was the key advantage for me – the need for multiple codebases/frameworks when developing an app as a small enterprise is a serious limitation. There is also the potential need for multiple offline applications/frameworks to support all the required platforms, so there can even be a need for maintaining multiple offline native app codebases.
  • PWAs are widely supported across browsers/platforms and are a standard.
  • A PWA greatly simplifies the installation process. One major issue with web sites which have a mobile app is the need to visit an app store and go through the install process before being able to continue. This really breaks the flow of use.
  • Web sites are really keen for users to install their app, as this offers functionality such as offline working and push notifications even when the app is not active/not in use. However, they typically resort to an pattern known as a full page interstitial ad. This interrupts the user, often with a full page overlay, right when they are interacting with content on the web site, in an attempt to force them to install the app – commonly called the door slam antipattern. This takes a number of steps, followed by actually running the app and returning to their previous context. A survey by Google showed that a significant percentage of users just dump the app installation and their web site activity completely when presented with the door slam.
  • PWAs have a much more seamless install process which is performed with a click directly within the browser. It does not involve visiting an app store, installing an app, and selecting options/enabling app permissions. Therefore significantly less users are put off by this.

 

Initial tryout/Proof of Concept

I branched the codebase for my existing Property Details application. This was a good example to try, as it has significant complexity:

  • Web application using the Angular framework (v13 at the current time).
  • PrimeNG component framework for angular is extensively used, including complex components such as the photo gallery
  • Dynamic scripting/script switching for configuration and PrimeNG theme switching.
  • Use of the angular APP_INITIALIZER to initialise/configure the app prior to angular bootstrapping, and use of angular/browser polyfills.
  • Use of the Webpack module bundler to bundle and install the application

I felt that if I could get this application to run seamlessly as a PWA with minimal effort and changes to the existing code, this would be a valuable proof of concept to justify taking PWAs forward.

My conclusion was very positive – whilst I had a number of technical/learning issues to overcome, I succeeded in getting the property details app to run as a PWA both online and fully offline in Chrome, edge, and Firefox, on both Windows and Android, with minimal code changes. The only code interventions were to add the necessary support for PWAs, which were minimal. None of the existing code had to be modified. I just needed to add a json PWA manifest file, a couple of scripts, and an icon image. I then just needed to modify the index.html page to declare the manifest and call the first of the scripts. Going forward, I will need to try the other PWA features such as background/offline sync, push notifications, and service worker to page/Window to service worker communication. I will also need to make some effort to smooth the app rendering to bring up a page quickly, especially when offline, as the current POC is somewhat jerky on occasion, with some delays on rendering. However, it works reliably as expected.

I also have not looked into the database functionality re offline/online use and syncing. For the database, it is tempting to stick with CouchDB, as PouchDB is available as a wrapper for IndexedDB and could be used for background sync with a CouchDB as I originally planned with my Ionic POC. One issue which needs exploring on this is the availability of CouchDB hosting – my ISP, Zen Internet, use CPanel which does not host CouchdB. If this is an issue I would need to either use IndexedDB with e.g. MySQL at the back end and my own abstraction/syncing between the two, or use continue to use PouchDB still with a MySQL back end – this whole area needs investigation.

I have therefore decided to look into using PWAs further as a replacement for my existing Ionic offline POC/tryout.

Comments Off on Progressive Web Apps