Frontend

Electron – Una nuova era per le applicazioni desktop

Lettura 8 minuti

Quando si vuole sviluppare un’applicazione desktop la prima cosa a cui si pensa è per quale sistema operativo svilupparla, quale linguaggio usare ed opzionalmente a quale base dati si ha bisogno di connettersi.

Il mondo dell’informatica intanto si sta evolvendo verso l’utilizzo di applicazioni web per la loro semplicità di distribuzione e aggiornamento, la compatibilità con ogni sistema operativo e la superfluità di un’installazione, allora perché non sfruttare questa tecnologia? Vengo ora a proporre un compromesso valido, Electron.

Dal sito ufficiale electron.atom.io, il suo slogan è “Build cross platform desktop apps with JavaScript, HTML, and CSS” ma come funziona?

Electron è una libreria open source sviluppata dal team di GitHub che permette di sviluppare un’applicazione desktop utilizzando le stesse tecnologie di un sito web unendo la potenza del browser Chromium, la flessibilità di Node.js e il più grande ecosistema di librerie open source al mondo: npm. In aggiunta a tutto questo la possibilità di pacchettizzare l’applicazione per Mac, Windows, e Linux con lo stesso risultato in termini di grafica e funzionalità.

Andiamo subito alla pratica per vedere come funziona

Innanzitutto partiamo da una cartella di progetto vuota, creiamo il file package.json come indicato qui:

{
  "name"    : "app-name",
  "version" : "0.1.0",
  "main"    : "main.js"
}

Andiamo ora a creare il file main.js, script che verrà eseguito da Electron, chiamato main process, in grado di creare l’interfaccia grafica della pagina web, che sarà quindi così composto:

// Caricamento delle librerie Node.js
const {app,BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')

// Mantiene la referenza globale dell'oggetto window, in caso contrario
// la finestra verrà chiusa automaticamente quando l'oggetto JavaScript
// verrà deallocato dal garbage collector.
let win

function createWindow() {
    // Creazione della GUI, non ancora visibile
    win = new BrowserWindow({
        width: 800,
        height: 600,
        show: false
    })

    // Indichiamo quale file HTML deve essere renderizzato
    win.loadURL(url.format({
        pathname: path.join(__dirname, "index.html"),
        protocol: "file:",
        slashes: true
    }))

    // Quando la GUI è pronta allora mostriamo la finestra
    win.once("ready-to-show", () => {
        win.show();
    })

    // Evento di chiusura dell'applicazione
    win.on("closed", () => {
        // Deferenziamo l'oggetto window
        win = null
    })
}

// Questo evento viene scatenato quanto Electron ha terminato il caricamento,
// alcune API possono essere chiamate ad inizializzazione avvenuta
app.on("ready", createWindow)

// Evento scatenato alla chiusura di tutte le finestre
app.on("window-all-closed", () => {
    // Su macOS le applicazioni e la loro barra dei menu rimangono attive
    // finché l'utente non forza la chiusara con Cmd + Q
    if (process.platform !== "darwin") {
        app.quit()
    }
})

// Su macOS è una pratica comune ricreare la finestra quando
// viene cliccata l'icona sulla dock e non ci sono altre finestre aperte
app.on("activate", () => {
    if (win === null) {
        createWindow()
    }
})

Infine creiamo il file index.html

<!DOCTYPE html>
<html>
  <head>
      <meta charset="UTF-8">
      <title>Applicazione di test</title>
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>

Ecco fatto, adesso vogliamo vedere il risultato. Dobbiamo ora installare il pacchetto npm electron, dunque installiamolo globalmente con il comando:

npm install -g electron

Al termine dell’installazione eseguiamo electron all’interno della cartella del nostro progetto usando il comando:

electron .

il risultato sarà questo:

Electron primo avvio

Aggiungiamo ora qualche modifica per facilitare lo sviluppo e prendere confidenza con la le API di Electron. Aggiungiamo all’interno del metodo createWindow() alcune righe:

...
    // Disabilitiamo l'antiestetica barra dei menu
    win.setMenu(null);

    // Aggiungiamo alcune scorciatoie da tastiera per
    // aprire gli strumenti di sviluppo e ricaricare la GUI

    var globalShortcut = require("electron").globalShortcut;

    globalShortcut.register("CommandOrControl+D", () =&gt; {
    	win.webContents.openDevTools();
    })
    globalShortcut.register("CommandOrControl+R", () =&gt; {
    	win.webContents.reload();
    })
...

e rilanciamo l’applicazione, adesso sarà possibile aprire i DevTools di Chromium per eseguire il debug e ispezionare l’HTML con la combinazione specificata Ctrl+D o Cmd+D su macOS

Electron developer tools

Adesso la domanda che sorge spontanea è “ok, è un sito web offline, ma ne passa di acqua sotto i ponti prima che diventi un’applicazione vera e propria” ed è proprio qui che entra in gioco Node.js.

Lo scope del javascript all’interno dell’HTML è fuso insieme a quello di Node.js, separato da quello del main process, ma è pur sempre Node.js. Aggiungiamo una funzionalità che un sito web non potrà mai avere: leggere il contenuto di un file. Ovviamente senza che l’utente lo abbia scelto da un input di tipo file.

Aggiungiamo un file nella cartella del progetto chiamato test.txt con un contenuto di esempio come alcune righe di Lorem Ipsum e aggiungiamo un file javascript /js/load-file-content.js con l’operatività per mostrare il contenuto:

// Caricamento delle librerie Node.js
const fs = require('fs');
const path = require('path');

// Lettura del contenuto del file
fs.readFile(path.join(__dirname, 'test.txt'), 'UTF-8', (err, data) =&gt; {
    if (err){
        throw err;
    }else{
        // Visualiziamo il contenuto del file
        var el = document.getElementById('file-content');
        el.innerHTML = data;
    }
});

Includiamo il file all’interno del index.html e aggiungiamo l’elemento html dove mostrare il contenuto

...
<body>
    <h1>Hello World!</h1>

    <p id="file-content"></p>

    <script src="./js/load-file-content.js"></script>
</body>
...

Il risultato sarà la visualizzazione del contenuto del file:

Electron contenuto file

Non ci sono limiti alle funzionalità che può avere la nostra applicazione, npm ha infinite librerie per fare praticamente qualunque cosa che un’applicazione desktop classica può fare, forse anche qualcosa in più. Vogliamo connetterci ad una base dati? ci sono centinaia di librerie Node.js, tra tutte Sequelize per PostgreSQL, MySQL, MariaDB, SQLite e MSSQL oppure Mongoose per MongoDB e tantissime altre.

In oltre Electron espone un’enorme quantità di API per le funzionalità più comuni delle applicazioni desktop, che potente trovare nella sua documentazione http://electron.atom.io/docs/tutorial/desktop-environment-integration/. Come per esempio notifiche desktop, documenti recenti, thumbnail toolbars (Windows), menu della dock (Mac), shortcut per il launcher di Unity (Ubuntu), barra di caricamento nella taskbar (Windows, Mac, Ubuntu) e tantissime altre.

Electron ci aiuta nello sviluppo aggiungendo alcune funzionalità come una gestione del Menu dell’applicazione, Localizzazione, Clipboard, un sistema per aggiornamenti automatici OTA e come abbiamo visto prima shortcuts da tastiera.

Come impacchettiamo l’applicazione?

Per poter distribuire l’applicazione bisogna crearne un pacchetto, utilizziamo uno strumento da linea di comando electron-packager. Installiamo globalmente il pacchetto npm:

npm install -g electron-packager

La sintassi da utilizzare è:

electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]

I sistemi operativi supportati sono:

  • linux
  • win32
  • darwin
  • all (tutti sistemi operativi)

Le architetture supportate sono:

  • ia32
  • x64
  • armv7l
  • all (tutte le architetture)

Per ulteriori informazioni sullo strumento seguire la documentazione relativa. Per rendere più semplice la fase di build consiglio l’utilizzo di grunt-electron che utilizza gli stessi parametri mappati con Grunt task runner.

Inoltre c’è la possibilità di creare un setup di installazione per Windows tramite il pacchetto electron-installer-windows e creare un pacchetto installante deb electron-installer-debian o rpm electron-installer-redhat per sistemi Linux.

La sicurezza del codice? Non si può compilare un sito web

Se trovate una soluzione per fare in modo che il software non venga copiato o ri distribuito  fatelo sapere ad Adobe, Microsoft e Autodesk, neanche loro hanno trovato una soluzione definitiva.

Nonostante questo si può sempre eseguire una procedura di uglify sul codice javascript e impacchettamento delle risorse con asar in modo da rendere il processo di reverse engineering il più complesso possibile. Pensate open source.

Quanto è usato Electron?

Sul sito web di Electron sono mostrate alcune delle applicazioni che attualmente usano questa tecnologia, tra queste spicca sicuramente Atom l’IDE di sviluppo per web developer, Insomnia un client per testare API insieme a Postman, GitKraken un GUI per per GIT, Slack famosissimo client di messaggistica real-time, WordPress.com pannello di amministrazione per siti WordPress, VisualStudioCode IDE di sviluppo, Kitematic GUI per gestione container docker e centinaia di altre applicazioni.

In conclusione Electron è un framework completo a potente tenuto costantemente aggiornato con release mensili e a volte settimanali in caso di fix di problemi, i suoi componenti Chromium e Node.js sono affidabili ed usati nelle loro versioni stabili.

Altre implementazioni

Ci sono diverse progetti che cercano di portare tecnologie web ad avere le funzionalità desktop.

Chrome, il celeberrimo browser di Google, utilizza una tecnologia molto simile per lo sviluppo delle sue App, come si può notare dalla documentazione developer.chrome.com/apps/first_app molti aspetti sono simili, un background script che ricorda molto il main process di Electron.

chrome.app.runtime.onLaunched.addListener(function() {
    chrome.app.window.create('window.html', {
        'outerBounds': {
            'width': 400,
            'height': 500
        }
    });
});

e un file html per creare la GUI, ma senza un’integrazione con Node.js.

È d’obbligo citare NW.js, aka node-webkit, un framework molto simile ma più asciutto nelle funzionalità. Molto interessante è la compatibilità con le Applicazioni Chrome.

Non si può non aprire una parentesi su Firefox OS, il sistema operativo sviluppato da Mozilla per dispositivi mobili utilizzando tecnologie web.

Non solo desktop, sono nati molti framework per applicazioni mobile basate su tecnologie web, il più famoso è Apache Cordova. A seguire Ionic Framework, uno dei progetti più imponenti in questo campo. Quest’ultimo ha sviluppato un IDE (oramai dismessa) chiamata Ionic Lab, indovinate un po’? Basata su Electron!

Sempre nel campo mobile aggiungo Appcelerator, una soluzione enterprise con strumenti di test, analytics, API per connettersi a svariate sorgenti dati  e molto altro.

Conclusioni

Personalmente ho usato Electron in un paio di progetti e ne sono rimasto molto soddisfatto, l’unico consiglio che sento di dover dare e di cercare di utilizzare dei framework javascript per lo sviluppo di applicazioni Electron per tenere una certa organizzazione: AngularJS, Backbone.js o Ember sono tutte validissime soluzioni.

Risorse

Articolo scritto da