ember and electron: a winning combination
I've been playing around with the Electron framework, formerly known as Atom Shell. It's a tool based on Chromium and io.js that allows you to build cross-platform desktop applications in JavaScript, and is most famously used as the framework for the [Atom][atom] text editor as well as the [Slack][slack] team messaging application. While Apple, Google and Microsoft all offer intriguing platforms and lots of cool features, there was something I needed to build quickly and didn't have the time or resources to find native OS X/Windows developers in which to help me complete it. So I turned to the tools I know best: JavaScript and, especially, Ember.js.
As I spoke about in my last post, I've also been playing around with Ember.js a lot in recent months. It's a really cool framework and I urge anyone, especially Rails developers, to check it out. Although Ember was designed for writing web applications, I was interested to see whether it was possible to write a desktop application that communicated with both local and remote services using Ember.js. Turns out, it sort-of is! I found an example electron app that helped me get pretty far in my endeavors, but it seemed to have some minor details missing, especially when you attempt to build the app into a package that can be distributed.
what ember-electron gets right
The ember-electron example app brought me a long way from where I
originally was, having zero idea on where to begin. Since Electron can
load URLs as well as local files, the ember-electron repo was able to
have the local Electron development app connect to an instance of ember server
by waiting for Ember to start and then booting Electron. It did
this in a very interesting way...here's the shell command that was used
as the "electron" job in node-foreman
:
$(while ! echo exit | nc localhost 5000; do sleep 1; done) &&
ELECTRON_ENV=development electron .
Unfortunately, this didn't quite work for me. I kept getting errors and
ending up in an infinite loop (on OSX Yosemite, running ZSH). However, by
breaking the command before &&
out into a script and storing it in
bin/wait-for-ember, I was able to get rid of the problems that I
had. So if you're having issues getting your Procfile to play nice with
your shell, I suggest using that strategy and seeing if it works for
you.
I also had to set locationType
in the config to "hash", as stated in
the notes for the ember-electron example app. This shouldn't really
mattter all that much as there's never going to be a situation (in my
app, anyway) that one will actually share a link with someone else in
this manner.
When you launch the Electron app for the first time, you'll get some
warnings in the dev tools console about Content-Security-Policy. To
mitigate these warnings, add the following to your
config/enviroment.js inside the ENV
object:
contentSecurityPolicy: {
'default-src': "'none'",
'script-src': "'self' 'unsafe-eval' 'unsafe-inline'",
'font-src': "'self'",
'connect-src': "'self'",
'img-src': "'self'",
'style-src': "'self' 'unsafe-inline'",
'media-src': "'self'"
}
This will allow you to run scripts and styles from within the page, and livereload won't trigger Content-Security-Policy warnings anymore.
All that aside, by following the steps laid out in ember-electron, you can get an Ember.js app (created using Ember-CLI) booted into Electron as a development server, to begin rapidly developing your app. LiveReload works as well, so when you make a change to the app/ folder in your repo, the Electron app will refresh the page and you will see your change live. Developing the UI for my desktop app has been easy, quick and fun!
the missing production-ready link
As stated previously, ember-electron is just an example and not really designed for production use. While the main.js file will work in a development server, attempting to run the app in a static HTML file will not work correctly as the proper files (such as main.js and package.json) are not in the dist/ directory like Electron expects them to be. You have to manually move those files over before compiling the Electron app, otherwise you will get a very strange error stating that your Electron app isn't valid, or you will just get a blank screen.
Thankfully, this kind of thing is easy to automate. I like to build my
JavaScript apps using a Makefile (I actually used one to
build this blog!), because the make
command is always
available, on pretty much any machine you want to be running this stuff
on. From that simple starter command, you can set off a chain of events
that eventually build and perhaps even launch your app. No need to write
any documentation on how to install, simply pull down the repo and run
make
to get yourself up and running.
Here's an excerpt from the Makefile I used to build my Electron app. The
make pkg
command will build the Electron app using an NPM script
called "package". But first, it will make sure the main.js and
package.json files are included into dist/, otherwise Electron
won't recognize the directory as a valid application when running in its
packaged form.
dist/main.js: dist
cp main.js dist/main.js
dist/package.json: dist
cp package.json dist/package.json
pkg: node_modules dist/main.js dist/package.json
@npm run-script package
The npm run-script package
command that make pkg
runs will run the
following shell command:
$ electron-packager . $APP_NAME --platform=win32,darwin --arch=x64
--version=0.30.1 --app-version=$APP_VERSION --app-bundle-id=$APP_BUNDLE_ID
--out=pkg
Note those "variables" in there aren't actually in the script, nor is
the "$" at the beginning of the code block. It's just to illustrate
where you'd interpolate your own values. You can also configure the
--platform
and --arch
switches to build for your own architectures,
but for what I'm working on it seems like just building for Windows and
OS X will suffice for now. That said, building for Linux is as simple as
appending its identifier to the --platform
switch and recompiling the
application. No sweat!
why ember?
In all my experience with JavaScript and especially its frameworks, I haven't come across one nearly as complete as Ember.js. With its added Ember CLI tool, it's relatively easy to do a lot of things that most people developing purely client-side applications have struggled with in the past. At this point in my life, I really don't have the time to be hunting for the latest and greatest mini-libraries for my build toolchain (I do that enough with Rails!)...instead, I'd rather have something pre-made for me so that when I want to "get down to business", I can do so with relatively little effort. Ember CLI, along with its excellent testing/building infrastructure and built-in development server, can also generate code and basically includes a generator for anything you'd want in an Ember application. Building this desktop app feels like building a Rails app, and that's something I've wanted for quite some time.
there's probably more to come
I'm just getting started with all of this...and will definitely post more about pitfalls later. For now, hit me back on Twitter if you have any advice or ideas on how to make this work better!