Building fully-functional Mac, PC, & Linux apps in Javascript, Pt. 1: Intro to Electron

Intro

This is the first in a series of posts that will introduce essential tools & strategies when architecting a JS app for native desktop platforms.

This series will not build up a hello world app, as quickstarts [1] and sample code exist for all major tools discussed. I encourage you to explore each at a pace and to a level appropriate for you and your build. Code samples will be provided to illustrate concepts & demonstrate usage only.

While code samples will be specific to Ember, the implementations are general enough to apply to all SPA's, regardless of framework choice (e.g. React, Angular, or none at all).

As with all hybrid projects, never stop considering how to leverage your architecture to minimize platform-specific code.

Series

Pt. 1 - Intro to Electron
Pt. 2 - GYP, node-gyp & NAN

Part 1: Intro to Electron

Table of Contents

What is Electron?

Electron [project, repo] is an OSS framework that lets you compose cross-platform desktop apps from Node.js APIs and Chromium windows. Originally extracted from Github's Atom editor, the project came into this world as Atom Shell, but got a spiffy new name in 2015 and is now called Electron (HAHA GET IT?).

Several large, well-known apps use Electron to offer native desktop experiences while leveraging ubiquitous web tech. The Electron project hosts a page full of names and logos and links [2], but here's a selection:

  • Atom (Github, OSS text editor);
  • Slack (messaging platform);
  • Brave (OSS browser);
  • Ghost (OSS blog platform);
  • Whatsapp (Facebook, messaging platform); and
  • Visual Studio Code (Microsoft, IDE).

Javascript, Javascript everywhere

Electron offers devs simultaneous control of the Node.js and Chromium runtimes in the same application. Teams use it to build native desktop apps with familiar web technologies, without sacrificing access to host OS APIs that remain sandboxed from web browsers (e.g. via the Chromium Sandbox, which Electron conveniently disables for us).

Electron provides JS APIs for several common host OS functions (e.g. app trays, keyboard shortcuts with or without OS focus...). Many of these APIs may only be called by Electron's main process, a headless Node "server"; Electron sandboxes these APIs from renderer (Chromium) processes, which run provided HTML + CSS + JS content and must message the main process via a global messaging interface.

Note that you can still require node modules from within renderer processes. Electron's sandboxed APIs (n.b. distinct from the previously-mentioned Chromium Sandbox) help devs avoid leaking objects that could retain access to long-lived host OS state (e.g. GUI functions), which are liable to cause user unhappiness and require a reboot.

I'd poke more fun at leaky browser code, but Electron's excellent Quickstart guide [1] already does.

Can Electron be used with ${spaFramework}?

Architecting an app is difficult; if you're considering an Electron build your app probably shares a lot of behavior across platforms, and would benefit from structure and standards. The big three JS frameworks have all been successfully paired with Electron, and support / resources exist for all three:

Ember

The Ember community benefits from @felixrieseberg's wonderful ember-electron [3], an Ember addon that exposes Electron build & packaging tools via Ember's familiar CLI. The addon also ships with:

  • A distinct requireNode function, so as not to confuse Ember's internal require;
  • Automatic livereload configuration for dev builds; and
  • Ember Inspector integration. Yay!

Felix gave an excellent intro to Ember + Electron at EmberConf 2016 [4].

React

@chentsulin manages a well-loved boilerplate project [5] for the React community, which ships with several scripts to simplify running your dev server(s) and packaging your builds.

There are also two popular UI component libs available, react-photonkit [6] & React Desktop [7].

Angular

Angular support seems limited to a couple of tutorials [8] that teach you how to use npm to install electron, and offer a reasonable enough pattern to isolate your Electron / Angular boundaries.

There's also a small helpers repo you should know about [9], but for which I can neither argue for nor against.

While I've already stated my preference for Ember, if you're building a net-new Electron project and considering Angular for your SPA framework, I urge you to reconsider. Regardless of how you feel about the framework itself (I'm personally not a fan), the limited tooling available will have you reinventing the wheel vs. working on your app.

When and how are Electron apps used?

Electron is handy when you're looking to develop apps that have full access to system resources, not just a web connection and some sweet, sweet browser APIs. For example, if you were building a notetaking app, you may want to persist files to & read files from the user's filesystem.

Let's write a simple Ember service that accepts a title and body, and returns a promise that rejects if not packaged with ember-electron, and otherwise attempts to read/write the corresponding file in a configured directory:

import Ember from 'ember';  
import ENV from 'notey-mcnoteface/config/environment';

const {  
  RSVP: {
    denodeify,
    reject
  },
  Service,
  isBlank,
  isPresent
} = Ember;

const DATA_DIR = isPresent(ENV.dataDir) ?  
  ENV.dataDir :
  "/usr/local/var/notey-mcnoteface/_data";
const ERROR_MESSAGE = isPresent(ENV.errorMessage) ?  
  ENV.errorMessage :
  "no access to host fs :(";

const fs = isPresent(requireNode) ? requireNode('fs') : null;  
const writePromise = isPresent(fs) ? denodeify(fs.writeFile) : null;  
const readPromise = isPresent(fs) ? denodeify(fs.readFile) : null;

export default Service.extend({  
  saveNote(title, body) {
    if (isBlank(writePromise)) { return reject(ERROR_MESSAGE); }
    return writePromise(`${DATA_DIR}/${title}`, body);
  },

  readNote(title) {
    if (isBlank(readPromise)) { return reject(ERROR_MESSAGE); }
    return readPromise(`${DATA_DIR}/${title}`, { encoding: 'utf8' });
  }
});

Is tooling available for the broader Electron ecosystem?

It's always worth pausing to consider third party deps, doubly so when they affect integral parts of your app. Beyond SPAs, Electron plays nice with many popular JS tools, and has a healthy developer ecosystem of its own:

Spectron

Spectron [10] is a utility for testing your Electron app's behavior in your test framework of choice. It exposes an Application class you can manipulate and observe with your test suite of choice. Use it to test behavior that depends on app state, e.g. post-start / pre-stop behavior, clipboard management, etc.

Devtron

Devtron [11] is a Chrome DevTools plugin that lets you monitor Electron-specific state and behaviors. Use it to inspect e.g. per-process dependency graphs & inter-process messaging.

Plugins

There are several OSS plugins available to soup up or speed up your Electron build. Some extend Electron's CLI with custom build options and hooks; others provide JS interfaces to host OS APIs.

A central repository of Electron plugins does not exist. As of this writing, two great places to look for Electron plugins are:

  • Electron Community [12], a list of helpful tools, plugins, boilerplate, and learning materials, blessed by the Electron team; and
  • Awesome Electron [13], another list of tools / plugins / boilerplate / etc, except this one is more extensive and maintained by @sindresorhus vs. blessed.

You can also write your own native plugins to take as much control of the host OS as you'd like. Parts 2 & 3 of this series will elaborate on related concepts.

But what are the downsides?

As with any SPA, when building Electron apps your JS must be a good citizen and use resources judiciously; you cannot rely on the request / response cycle to flush and reset window state. Good Electron developers will:

  • Maintain mechanical sympathy with the underlying engine / rendering system;
  • Use memory consciously; and
  • Hunt memory leaks like your favorite apex predator, feasting on their delicious flesh once discovered, letting the smug aftertaste linger a half-hour or so longer than necessary.

Except you only have to do this once, in one language, vs. across every platform and language you deploy to / with.

Additionally, your code has full access to the host OS. Take care to protect your use of native APIs, and do not execute remote code (at least, without a very good reason, plus two scoops of trust in every box). If you need to load remote content in your Electron app, consider using a <webview> tag [14].

Conclusion

  • Electron is a popular OSS JS runtime that lets you build native Mac, PC, & Linux desktop apps with HTML + CSS + JS;
  • You can use your favorite SPA with Electron (but don't use Angular ;)
  • Use Electron when you need access to host OS systems & services, e.g. the filesystem, the system tray, an independent app process...
  • A broad set of tools exists for Electron developers
  • Be extra-cautious when building Electron apps; with great power comes great responsibility.

Credits

Much thanks to @alexblom [Twitter, Github] & @felixrieseberg [Twitter, Github] for sharing feedback on early drafts.

Links & References
  1. Electron Quickstart - http://electron.atom.io/docs/tutorial/quick-start/
  2. Apps on Electron - http://electron.atom.io/apps/
  3. ember-electron - https://github.com/felixrieseberg/ember-electron
  4. Building desktop apps with Ember and Electron (@felixrieseberg's EmberConf 2016 talk) - https://www.youtube.com/watch?v=_uA5LZk2vmQ
  5. Electron React Boilerplate - https://github.com/chentsulin/electron-react-boilerplate
  6. react-photonkit - https://github.com/react-photonkit/react-photonkit
  7. React Desktop - https://github.com/gabrielbull/react-desktop
  8. Angular + Electron tutorial - http://electron.rocks/electron-angularjs/
  9. Angular + Electron helpers - https://github.com/ozsay/angular-electron
  10. Spectron - https://github.com/electron/spectron
  11. Devtron - https://github.com/electron/devtron
  12. Electron Community - http://electron.atom.io/community/
  13. Awesome Electron - https://github.com/sindresorhus/awesome-electron
  14. <webview> tag - http://electron.atom.io/docs/api/web-view-tag/

aidan nulman

partner @ isle of code. hacks with ember.js & btle (e.g. ibeacons).

About Isle of Code:

We’re a Javascript/Ember firm in Toronto + Chicago, est. 2012. We provide on-demand development for Mobile Apps, Websites, iBeacons/ hardware, existing codebases and team augmentation / Ember leadership.

read more