Desktop App Development with Electron

Desktop apps are always fun to build and use. They are the most powerful way of communicating with computers. This post will give you a basic understanding of using Electron for creating cross platform desktop apps.

In the old days, I used Netbeans to quickly create desktop apps with Swing. The jar file could run on Windows, Mac or Linux as long as they had a JVM installed. There were apps that could export these jar files into a Mac app or window installer. You wish you never had to read or write that Java Swing code.

Now, my goto tool is Electron, and its just awesome. If you don’t know what it is, just read through, you may realize you already have all the basics knowledge to work with it, if you can write some JS, CSS, HTML code.

Web development is something every developer is bound to come across. Any experienced developer would certainly have had to write some HTML, CSS, Javascript code at some point of time. I spent a lots of time developing single page applications, that behave similar to desktop app, but runs inside a browser.

Electron now lets these single page app run as standalone apps on a Mac, Windows or Linux machines and talk to operating system resources.

How electron works ?

Electron uses a combination of two powerful and open source technologies Node JS and Chromium. Both these technologies are themselves very mature and have a proven track record of cross platform support.

While node gives power to manipulate the underlying system in any possible way, chromium allows creating GUI with HTML, CSS, JS that run flawlessly across the operating systems.

So, you can do anything with an electron app, that you can do with Node JS running locally and create GUI for it exactly the way you would create for a webpage that runs in a Chrome browser.

Lets look at some core concepts that will be useful before you start building your Electron app

Processes

There are two kind of processes in an Electron app

  • Main Process : This runs in the node environment and is the entry point of an Electron app. This process can talk to OS APIs and launch a window, which renders the GUI based on HTML, CSS and JS.
  • Renderer Process : These processes are created each time you create a GUI window (called BrowserWindow) from main process. In Chrome (and Chromium), every tab represents a process running on machine. Similarly every GUI window in Electron is a process that works just like the Chromium tab.

Just like any browser environment, these renderer process do not have ability to read files or talk to OS APIs (as these are simply Chromium windows). To manipulate files and talk to low level OS APIs, the rendered process depend on the main process.

Following is a simple example of creating a BrowseWindow from a main process

// In the main.js
const {BrowserWindow} = require('electron')
let win = new BrowserWindow({});
win.show()

Communicating between processes

Main process and Rendered process have their own separate runtime (node and chromium). So communication between them has certain restrictions. We need to use IPC or remote module to make them talk to each other.

Remote Module

The simpler way of doing this communication is using remote module, which provides RPC (remote procedure call) styled communication.

These allow functions from main process to be invoked from renderer process(code in webpage). From the webpage you can pass on callbacks to the main process as well, but beware, main process should not consume the returned value of these callbacks, which is deliberately prohibited to avoid deadlock conditions.

Define a method in main.js

// main process mapNumbers.js
exports.withRendererCallback = (mapper) => {
return [1, 2, 3].map(mapper)
}

exports.withLocalCallback = () => {
return [1, 2, 3].map(x => x + 1)
}

Invoke method from the webpage as :

// renderer process
const mapNumbers = require('electron').remote.require('./mapNumbers')
// main process will not receive returned value of callback
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1)
const withLocalCb = mapNumbers.withLocalCallback()

console.log(withRendererCb, withLocalCb)
// [undefined, undefined, undefined], [2, 3, 4]

IPC

The remote module itself uses IPC internally. IPC uses an event model to send message and data back and froth.

Define IPC listener in main.js as

// In main process.
const {ipcMain} = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.sender.send('asynchronous-reply', 'pong')
})
ipcMain.on('synchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.returnValue = 'pong'
})

In you webpage :

// In renderer process (web page).
const {ipcRenderer} = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"
ipcRenderer.on('asynchronous-reply', (event, arg) => {
console.log(arg) // prints "pong"
})
ipcRenderer.send('asynchronous-message', 'ping')

Sharing data between windows

Because this windows behave just like tabs in Chromium browser, so you can you localStorage, sessionStorage or indexedDB to share information between them.

Also you can create global variables in main process and then read/modify them from GUI (web pages)

// In the main process.
global.sharedObject = {
someProperty: 'default value'
}
// In page 1.
require('electron').remote.getGlobal('sharedObject').someProperty = 'from page 1'
// In page 2.
console.log(require('electron').remote.getGlobal('sharedObject').someProperty)

Bundling and Packaging

There are three popular tools for packaging apps with Electron

  • electron-builder
  • electron-forge
  • electron-packager

The electron-builder is my favorite. It also comes with electron-update that supports auto updating apps, by publishing on Github. Even though the Electron itself supports auto updating but you will need to create a server to distribute the executables.

An electron app can do everything that any other desktop app can. You can create window installer, create icons for app, run them ad daemon, take screenshots etc. Give it shot, I am sure you will love it.

Like what you read? Give Nishant Singh a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.