The most obvious Electron alternative on macOS/iOS

Recently I see some discussion about Electron on macOS (John Gruber, Dave Verwer). I have some experience in the field and will share my view on the topic and a practical introduction here.

Why web technologies?

In general web technologies are a de facto standard, not only in classic web browsers. There are some valid reasons for that:

  • It is cross platform
  • It is simple to learn and the majority of developers had contact with HTML, CSS and Javascript
  • It is a mature, stable, powerful and feature complete** environment
  • It is "the internet"
  • It is cheap

So it takes no wonder to understand that developers, but also product managers, have some sympathy for the platform.

About Electron

As soon as server driven service wants to create a desktop application it seems natural to start an Electron project and point to the service's URL. This of course has no additional value for the user besides having the app in the Dock.

But Electron can do more, as Visual Studio Code and others demonstrate. As soon as the app makes use of the file system, native menus and other features, it comes closer to a real desktop app.

The look and feel these days isn't that important anymore since the skeuomorphic design world transformed into a flat one.

But there are some disadvantages as well:

  • Size: The distributed app package comes with the whole browser engine included
  • Security: You need to trust Electron which builds on Chromium which is maintained by Google
  • Performance: Even though web technologies became ridiculous fast they are still slow in some area. You can add native modules for such tasks to Electron, but then you already left the comfort zone.
  • Desktop: It only works on macOS, Windows and Linux. For iOS and Android you need to look out for other solutions, which of course exist.

How to use WKWebView

I recently started a new project called Collect App and also had to make a decision of which technologies I'd like to use. I had written many pure native apps like my latest ones Receipts and PDFify, but I have to admit I like the latest Javascript Syntax very very much.

image-20181220111209125

Since I have a lot of code I would like to reuse (OCR, Scanner Dialog, Share Extensions, PDF Logic) I really just need it for the UI. So Instead of taking Electron and trying to put my stuff in there, I have chosen to use WKWebView and here is my story:

Bridging

First of all I need to set up a connection between the native (Objective-C) and web view (Javascript) sides, which I named: Bridge.

Sending to Javascript is easy as calling evaluateJavaScript:completionHandler: For the other way I use WKScriptMessageHandler.

As you can see there is some asynchronous code involved and this is, where my bridge code comes into play. It defines some loose protocol of how the data that is send needs so be structured, for example:

{
    action: "fetchData",
    responseAction: "tmp-123",
    payload: { skip: 0, limit: 100 }
}

This would basically work in both directions and mean, that fetchData should be called and the response be send to responseAction. These actions will registered with the bridge code. Their return value will be the payload for the response call. The response actions will temporarily be registered and deleted after successfully having received a response or having reached a timeout.

Native UI

Using this technique it is quite easy to trigger native things like menus or popovers. I can also easily access my existing components. Also, all the database handling is done on the native side.

Custom schemas

This works well for data that does not exceed some Kilobytes, but for providing images it would slow down the process. Therefore, I also implemented WKURLSchemeHandler, not only for images but also for the HTML pages that make the basis for the web app part.

Another nice benefit from it is that it can be used to trigger heavy computation resulting in large data. In the case of Collect App I use it to render PDF on the fly to PNG.

JSContext

But Apple does not only offer WKWebView for hosting Javascript, it also provides the slick JSContext for headless code. I also make use of that for example to extract data from the notes to build the full text index on the native side.

With some fine-tuning you can nicely catch exceptions and pass logging to the native log for better debugging experience. I was also able to add support for require to dynamically load modules into the JSContext.

macOS & iOS

And the best: all this applies to iOS as well as to macOS. You can actually reuse the whole glue code. With Electron it is also all or nothing where with this approach you may decide view by view if you want to use it or not.

Screen Shot 2018-12-20 at 11.15.15

Vue.js

For the discussion about Electron and WKWebView it does not matter, which frameworks to use on the web side, but I just want to express my love for Vue.js here 😍 For a macOS developer used to bindings this is a nice and powerful tool to feel right in the shiny web world.

Try yourself

Would you like to see it in action? No problem, you can, by subscribing to the early preview of my new app Collect:

Subscribe for preview of Collect App

 

Update 2018-12-27:

This article has received great feedback on Hacker News with very interesting and often profound discussions.

In the previous version of this article I said, Electron is based on Chrome which is owned by Google, which was not correct. It is based on Chromium which itself is the basis of Chrome, but the influence of Google on the code is still strong. Even though I don't think Google is intentionally putting bad code into the project my intention by mentioning this point was, that the code basis is so large that usually nobody using it for a web project would ever do a code review before using it. The same of course applies to WKWebView, but since it is part of the operating system Apple ships, I think if you trust the OS at this point you can trust this component as well.

One argument I often heard has been: Your approach is not cross platform. And indeed this particular native implementation only works in the Apple ecosystem. Similar approaches in other environments can be achieved with reasonable effort. Projects like Cordova or Capacitor are built this way and there are more examples available to start with. But this is just a thin layer and the web code is the one that will grow and this is truly cross platform. In case of Collect App I might even build Windows and Linux clients on Electron first, but the web code will still stay the same for all destinations apart from some glue code for UI like menus.