JoT
Note
coding

Distribution Mechanism for Arrow

May 2021

Huidong Yang

Arrow is built with web techs (mainly Elm), with which I'm mostly happy. But as an app currently living in a web browser, there exist a few significant risks and disadvantages, the most notable incident being my unknowningly deleting the app data (stored in IndexedDB) by merely using Firefox's "Forget About This Site" on the localhost serving Arrow. I didn't think IDB would be a target of the site-wide cleanup mechanism. Later I came to understand such aggressiveness as a reaction to the trend of websites increasingly taking advantage of a wider array of storage systems to identify and track people. Unfortunately, for an app that uses IDB as the main persistence mechanism, the privacy protectiveness of the browser makes it a harsh environment to offer a strong protection against data loss, particularly due to unintended browser cleanup. This is clearly the #1 drawback.

(I heard about certain API work in progress to declare persistent storage for a given site, but I haven't followed the news recently regarding its standardization across browsers. Todo. Update: after I looked into it, I realized that it wasn't meant to protect data from user-triggered deletions, but rather merely from automatic data eviction by the browser. So I suppose setting up automatic, frequent data backup remains to be the practical guard against the risk.)

The second, minor drawback of web-based app distribution is, obviously, the requirement of an internet connection. Now Arrow uses no remote server as a backend, so all it takes to run the app is downloading the app files to the local disk, not much different from how desktop apps are distributed, except 1) web apps are much smaller (pro), and 2) the downloaded files, although may be cached, are not reliably persisted by default (con).

(Nonetheless, persistent offline access to the app files can be enabled by explicit code using the Cache API, deployed via a service worker.)

But that wasn't the history of my thinking process. Before I ever considered any WebAPI-based or browser-specific solutions, the first idea that came to my mind was to look for effective ways to package a web app as a standalone executable for desktop distribution. This intuition is only natural though, for desktop apps are known for their offline-sufficiency, and because each app typically has its own dedicated data directory, accidental data deletion is much less likely to occur. (Windows Registry is an exception, and it's no surprise that its usage has been declining.)

Can web apps be made to be like the good old desktop apps such that users don't have to upload their data to some servers controlled by the developers and then worry about whether they can be trusted? That is, for complete offline apps like Arrow, can the web browser ever be an ideal environment? As far as my experience goes, lumping together trusted, valuable data and wild, disposable caches, all accessed and controlled by a single executable, is a major risk factor. It isn't providing adequate isolation, in particular, there is no standardized, universal mechanism today to mark a site not only trusted, but its data critical and thus demanding extra level of protection.

One might ask, why bother with web apps? Perhaps all they're meant to be is a client and local cache to some server-stored data? Why not stick to traditional desktop apps? I know this is big topic (cf. Sublime Text vs. VS Code), but a short answer is, web techs seem to be the single best cross-platform (indeed, platform-independent) toolkit for making GUI apps, with the only disadvantage being out-of-box raw performance (but watch Wasm).

  • Power & Ergonomics: with the desktop counterparts, there is the mutual exclusion of high-level, OS-standard widgets (easy) XOR low-level, custom components (less easy); with web, creating custom UIs can be made ergonomic! This curve-bending achievement that breaks this dreadful mutual exclusion is, in my opinion, due to Elm and Elm-UI; frankly, they are the reason I stick to web techs for making apps. I appreciate craftsmanship very much, but it would be much harder psychologically to deal with all the messy code for the sake of satisfying the geeky/artistic soul.

  • Reach & Security: precisely because we're not distributing executable files (namely the traditional desktop approach), we don't have to deal with the idiosyncrasies of various operating systems and app stores. We delegate all this to the ubiquitous web browser. As a result, web apps can reach people via any device instantly, no competition here. (Re: security: no doubt the web is full of danger, but the silver lining is that web browsers consequently pay much more attention to their vulnerabilities than most other desktop apps do.)


Now I did dip my toe in the water to test out the desktop approach. The "webview" approach (e.g. Tauri) was my first candidate because it could generate much smaller executables for distribution. But the major drawback is that you lose the consistency guarantee that the app is run in the same browser engine regardless of the OS.

Additionally, unlike the Electron approach where a pre-built binary package can be downloaded and then modified to produce a rebranded app, "webview"-based apps must be manually built by the developers themselves, on every target OS. Therefore, it takes a lot more system-wide toolchain installations to just get started. Taking Tauri as an example, besides setting up Rust and Node.js, it requires Xcode and gcc on macOS, Visual C++ Build Tools and WebView2 on Windows, and an assortment of libraries (including gtk, webkit2gtk, etc) on Linux.

So I ended up trying Electron first. Besides imposing much fewer toolchain prerequisites, it took a fairly minimal amount of boilerplate code (just JS) to get the app running.

Unfortunately, it didn't work.

Uncaught (in promise) Error: Browser.application programs cannot handle URLs like this:

file:///Users/huidong/i/code/p/arrow/electron/index.html

What is the root? The root of your file system? Try looking at this program with elm reactor or some other server.

An Elm app using Browser.application doesn't work with Electron, because elm/url doesn't support the file:// scheme (issue). Note that Browser.document and simpler programs do work, since elm/url is not involved there. But in the case of Arrow, onUrlChange is not something I'm willing to give up.


Sad, right? But that made me think. Is desktop really the optimal distribution mechanism for Arrow?

The thing is, Arrow needs no desktop-specific features such as directly communicating with the OS. The browser offers enough APIs to enable data storage and export/import. To recap, the cons of browser-dwelling apps are the following:

  • Accidental data loss by unintended browser cleanup (solution: navigator.storage.persist() good old data backup)
  • Internet connection required for access (solution: the new Cache API)

The "Progressive Web Apps" (PWA) initiative incorporates many of the key ingredients that can make web apps feel and behave like desktop apps, without the need to build actual desktop apps. I never took the idea seriously to be honest, and I dismissed it all along as a Chrome-specific novelty/gimmick. But now I'm surprised to see a pretty good alignment with my own goal of making Arrow more desktop-like, but without the hassle that comes with distributing standalone executable files across multiple platforms (e.g. signing certificates, large file hosting, app store-related cost, even dealing with antivirus false positives).

But of course, the jury is still out. Because both sides, desktop vs. web, are evolving.