Strumenti Utilizzati

Cordova e plugins

Per realizzare l'applicazione è stato utilizzato Cordova 3.4.1 ed i seguenti plugin:

Ionic

Per realizzare l'interfaccia grafica dell'applicazione è stato utilizzato Ionic un framework open source che utilizza AngularJS e fornisce agli sviluppatori componenti (HTML/CSS/Javascript) ed alcuni plugin per Cordova.

I componenti forniti hanno directive da utilizzare nelle view HTML ed API da utilizzare nei corrispondenti controller.

Ionic è un framework molto giovane e nella fase iniziale di realizzazione e diffusione. All'inizio della scrittura dell'applicazione era stata da poco pubblicata la prima beta pubblica, ad oggi è stata rilasciata l'ottava beta.

Per l'applicazione sono stati utilizzati i seguenti componenti:

  • ion-nav-view: componente che tiene traccia della storia di navigazione dell'utente all'interno dell'applicazione fornendo animazioni tra una view ed un altra. Utilizza UI-Router per gestire i vari stati dell'applicazione

  • ion-nav-bar: per gestire l'header di ogni view inserendo titoli e pulsanti personalizzati

  • ion-tabs: permette di utilizzare il layout a tabs ed associare ad ogni tab una diversa view

  • ionicModal: per la gestione di view modali

  • ionicPopup: per notificare l'utente

Estensioni AngularJS

Sono state utilizzate anche le seguenti estensioni per AngularJS:

  • Angular-google-maps : fornisce directives per utilizzare google maps all'interno di applicazioni AngularJS

  • Bindonce: utilizzato quando possibile per aumentare le performances

A causa del two-way data binding di AngularJS la directive ng-repeat causa dei deterioramenti di performances poiché AngularJS utilizza un meccanismo di dirty checking per controllare se sono state effettuate modifiche negli elementi. Utilizzando bindonce viene “rotto” questo two-way data binding per gli elementi specificati, e dopo aver fatto il rendering degli elementi nella view AngularJS non controllerà eventuali modifiche. Ho utilizzato bindonce nella lista di fotografie visualizzate nella timeline.

Le future versioni di AngularJS dovrebbero utilizzare il metodo Object.observe() (non ancora supportato da tutti i browser) per velocizzare queste operazioni ed il team di AngularJS ha dichiarato di avere già ottenuto un incremento delle prestazioni del 20-40%.

Struttura dell'Applicazione

Per la scrittura dell'applicazione ho realizzato tre moduli AngularJS, ed ho organizzato i file in modo da avere un componente in ogni file separato, definendo tutti i moduli nel file app.js e utilizzando il metodo getter angular.module per ottenere in ogni file il modulo desiderato per poterci definire e registrare il componente.

Services

Il modulo MyPhotoDiary.services è così definito:

angular.module('MyPhotoDiary.services',[]);

ed è richiamato in ogni file dentro la cartella scripts/services per poter definire un servizio in ogni file separato (ho fatto questa scelta perché preferisco avere più file piccoli ognuno con limitate responsabilità, poi in fase di deploy i fari file .js vengono concatenati e il file risultante viene minimizzato).

Ho realizzato i seguenti services:

  • CameraService: utilizza le API fornite dal plugin Cordova Camera per scattare fotografie e restituisce una promise.

  • GeolocationService: utilizza le API fornite dal plugin Cordova Geolocation plugin per ottenere le coordinate gps e restituisce una promise.

  • PicturesService: gestisce le fotografie dell'applicazione, utilizza il local storage per memorizzare la descrizione, il path su filesystem locale e le coordinate di ogni fotografia. Per l'operazione di rimozione di fotografie dal filesystem utilizza StorageSettings e restituisce una promise (le operazioni su filesystem in Javascript sono asincrone). Emette l'evento NewPicture nel rootScope ogni volta viene realizzata una nuova fotografia, in modo tale che qualunque controller possa gestire questo evento una volta sottoscritto. (AngularJS rilascia un controller se l'utente non è nella view correlata, quindi ho scelto di avere un evento globale in modo tale da poterlo gestire indipendentemente dalla view corrente dell'utente)

  • SettingsService: memorizza le preferenze dell'utente utilizzando il local storage.

  • StorageService: utilizza le API fornite dal plugin Cordova File e permette di memorizzare e cancellare le foto dal filesystem. Per le operazioni restituisce una promise.

Controllers

Il modulo MyPhotoDiary.controllers è così definito:

angular.module('MyPhotoDiary.controllers',['MyPhotoDiary.services']);

e come già spiegato per il modulo relativo ai services è richiamato all'interno di ogni file dentro la cartella scripts/controllers.

Sono stati realizzati i seguenti controllers:

  • MainNavBarController: gestisce la navigation bar presente in ogni view dove è situato un pulsante per scattare una nuova fotografia. Utilizza i services CameraService, GeolocationService, PicturesService e StorageService per catturare e memorizzare la nuova foto. Usa anche ionicModal per mostrare all'utente una view modale per confermare o scartare la nuova fotografia, e ionicPopup per visualizzare eventuali errori.

  • PlacesController: responsabile della creazione dei marker da visualizzare sulla mappa. Utilizza GeolocationService per centrare la mappa nella posizione corrente dell'utente e PicturesService per recuperare le foto da mostrare. Gestisce l'evento NewPicture reindirizzando l'utente alla view timeline.

  • SettingsController: permette di scegliere di quante foto effettuare il prefetch per essere visualizzate nella timeline e di eliminare tutte le foto dell'applicazione. Usa PicturesService per calcolare quante fotografie sono attualmente memorizzate e SettingsService per memorizzare le preferenze utente. Anche questo controller gestisce l'evento NewPicture reindirizzando l'utente alla view timeline.

  • TimelineController: gestisce la condivisione e l'eliminazione della foto selezionata nella corrispettiva view. Inoltre carica ulteriori foto e le aggiunge alla view non appena l'utente raggiunge il limite inferiore dello scroll della view, infatti per motivi di performances non vengono immediatamente caricate tutte le foto nella view. Utilizza PicturesService per richiedere le foto memorizzate mano a mano che l'utente scorre la view e SettingsService per sapere di quante foto effettuare il prefetch. Gestisce l'evento NewPicture aggiungendo la nuova foto in cima alla view e tornando all'inizio della lista di fotografie.

Modulo Principale

Il modulo MyPhotoDiary è così definito:

angular.module('MyPhotoDiary', ['ionic', 'MyPhotoDiary.controllers', 'MyPhotoDiary.services', 'google-maps', 'pasvaz.bindonce'])

Ed è configurato nel file app.js specificando per ogni stato dell'applicazione (usando UI-router) il rispettivo nome, url, template e controller.

Gli stati sono:

  • tabs: stato astratto con template tabs.html. Uno stato astratto non può mai essere direttamente attivato, sarà attivato uno dei suoi stati figli.

  • tabs.places: mappato su url /places utilizza il template places.html ed il controller PlacesController.

  • tabs.timeline: mappato su url /timeline utilizza il template timeline.html ed il controller TimelineController.

  • tabs.settings: mappato su url /settings utilizza il template settings.html ed il controller SettingsController.

All'avvio dell'applicazione viene impostato lo stato tabs.timeline.

Views

I template di ogni view sono salvati all'interno della cartella templates e sono:

  • confirmPhoto: una view modale che visualizza la foto appena scattata e permette di confermarne il salvataggio oppure di scartarla. E' gestita da MainNavController.

  • places: contiene una google map e un marker per ogni foto nella posizione in cui è stata scattata. Utilizza la direttiva <google-map> fornita da Angular-google-maps.

  • settings: attraverso uno slider permette all'utente di scegliere il numero di fotografie di cui fare il prefetch nella timeline ed ha un pulsante per eliminare tutte le foto memorizzate.

  • tabs: visualizza tre tab, uno per lo stato tabs.timeline, uno per lo stato tabs.places e uno per lo stato tabs.settings.

  • timeline: mostra in una lista in ordine cronologico le fotografie memorizzate e sotto ognuna di essa due pulsanti, uno per eliminare ed uno per condividere la foto.

Tools

Per velocizzare il workflow sono stati utilizzati alcuni tool open source, ne riporto in seguito una breve descrizione per ognuno.

Grunt

Grunt è un task runner scritto in Javascript con molti tool e plugin resi disponibili dalla comunità, tra i plugin che ho utilizzato segnalo bower-install che inietta le dipendenze gestite con bower nell'HTML, concat che concatena diversi fine in uno unico, ngmin, cssmin che comprime i file CSS, uglify per minimizzare il codice Javascript e htmlmin che minimizza il codice HTML.

È installabile attraverso npm (package manager di node.js).

Bower

Bower è un package manager per componenti web. Per l'applicazione ho specificato come dipendenze AngularJS, Ionic, angular-google-maps e angular-bindonce. Bower provvede a scaricare in locale l'ultima versione (o la versione specificata) delle dipendenze, in modo tale che facendone il deploy sul dispositivo non sia necessaria alcuna connettività di rete.

Anche bower è reperibile attraverso npm.

Yeoman

Yeoman è un tool che automatizza il setup degli strumenti precedentemente descritti grazie a generator resi disponibili dalla comunità (attualmente ci sono più di 700 generators).

Per lo sviluppo di questa applicazione ho utilizzato il generator chiamato generator-ionic disponibile in maniera open source all'indirizzo https://github.com/diegonetto/generator-ionic.

Anche yeoman è reperibile con npm.


Daniele Campogiani

Software Engineer