Lessons Learned Building An Offline-Only PWA
Here are my initial takeaways from a week, heads-down, building Birthday Alert, a Progressive Web App (PWA).
As I mentioned in the post on my initial PWA experiment, what I've really been after is a viable alternative to a native mobile app. Specifically, the equivalent of one which would be downloaded from an app store, installed on a phone, and then used to ONLY locally store data the phone owner personally enters into the app (with the ONLY reason to return to the app store to get a bug fix or an infrequent upgrade).
I found little online discussion of truly "offline-first" PWAs so, instead, relied almost entirely on the helpful, but more generic, MDN and Google documentation.
Chromium-based browsers are the best bet for now, considering the widely-varying levels of support for PWAs across operating systems and browsers. To ensure my PWA works as well as possible, I'm taking the approach of requiring...errr...strongly encouraging customers to use Chrome on whatever sort of device (desktop or mobile) they're choosing to install the app on.
Key to Birthday Alert's functionality is the ability to generate regular reminders for the customer, triggered by the data they've entered into it. Though device system notifications are accessible, without employing a server mechanism, you are unable to trigger notifications when the app is not running (though it needn't be running in the foreground, at least). Which is annoying, but I was able to implement a passable solution. (As I recall, making use of device system notifications, for fully native apps, was not as straightforward as I assumed it would be, either!) Of course, this shortcoming makes me want to implement a server component to the app (see more related temptation later in the post) but the point was to make it truly offline-only.
Because I had to continuously make changes, re-test, identify what to tweak next, and repeat, I needed to prevent caching that could be avoided in the first place, as well as clear out anything that did get cached, and blow away the app itself, each iteration.
Chrome DevTools (View >> Developer >> Developer Tools) are fantastic for all manner of web development so they were key to my regimen; first and foremost, it was essential to turn off caching (via/Within Settings >> Network >> "Disable cache (while DevTools is open)" ).
Beyond that, to ensure that my latest code was running, each time I updated it, I also needed to uninstall the app (by navigating to "chrome://apps", right-clicking on the app icon, selecting Uninstall, in the Remove "[APP NAME]?" modal selecting the "Also delete data from Chrome?" checkbox and pressing the "Remove" button), remove all of its data (via Chrome >> Clear Browsing Data, in the Clear Browsing Data modal selecting the Advanced tab, setting Time Range to "All time", selecting ALL checkboxes, and pressing the "Clear data" button), and close Chrome directly.
Doing each of these steps, every iteration, may well be overkill but I settled on this routine upon discovering that the power and persistence of service workers (stay tuned/look for more about them in a future post) also proves annoying when iterating. I went so far as to build the service worker cache with an incrementing numerical suffix (and a correpsonding "name" and "short_name" in the app manifest) to make it obvious to me, at a glance, that my newest code was running.
As for other challenges I faced and overcame, the most pesky involved getting the app icons working properly. It seems they must be located in the same directory as the manifest file; this is not documented anywhere that I could find so I did a bunch of fiddling with path specification before resigning myself to moving them out of their own subdirectory and into the root.
Without any backup on a server or elsewhere, I'm definitely concerned about data integrity or lack thereof, though IndexedDB seems pretty solid and is easy to work with (especially when wrapped in Dexie.js). Which is sort of funny considering I'm very much an (outspoken!) "always browse in incognito and retain no data on browser close" kind of guy! In this case, the tact I'm taking, as a user of my app myself, and which I'll suggest others do too, is use Chrome ONLY for accessing offline-first PWAs, never clearing data (so the data you've added to the apps remains) and use a different browser for all other web travels (clearing cookies to your heart's content).
This does highlight the (clear and rather urgent) need for SOME way to back up data, in case Chrome gets automagically updated and the persistent storage is cleared. The logical, longterm solution to this seems to me to be offering a (paid) option from my website, to back up customer-entered data on a server (which then leads to additional, related upgrade possibilities), but a quick-and-dirty, "emergency" option might be to merely provide a page, within the PWA, that dumps the data contained in the (IndexedDB) store on a page, as (JSON? CSV?) formatted text, from which it can be copied and pasted somewhere, for safe keeping by the customer.
Hopefully, my mentioning this all will at least somewhat ease your offline-only PWA journey, if you choose to take one.
That's all for now; though it's early, I am encouraged by what I learned in building this and intend to keep working on Birthday Alert as well as building additional PWAs (offline-only and beyond).