Prototipizzazione e generazione automatica della ...

146
I POLITECNICO DI MILANO Scuola di Ingegneria Industriale e dell’Informazione COMPUTER SCIENCE AND ENGINEERING A.A. 2017 / 2018 Prototipizzazione e generazione automatica della componente Controller per applicazioni mobili cross - platform Relatore: LUCIANO BARESI Tesi di: ALBERTO OSIO [email protected] – 873487

Transcript of Prototipizzazione e generazione automatica della ...

I

POLITECNICO DI MILANO

Scuola di Ingegneria Industriale e dell’Informazione

COMPUTER SCIENCE AND ENGINEERING

A.A. 2017 / 2018

Prototipizzazione e generazione automatica

della componente Controller

per applicazioni mobili cross - platform

Relatore:

LUCIANO BARESI

Tesi di:

ALBERTO OSIO

[email protected] – 873487

II

Indice

INDICE DELLE FIGURE ................................................................................................... IV

INDICE DELLE TABELLE ................................................................................................... V

SOMMARIO .............................................................................................................. IX

ABSTRACT ................................................................................................................. X

1 INTRODUZIONE ..................................................................................................... 1

2 CONTESTO GENERALE ............................................................................................. 5

2.1 Stato dell’arte ......................................................................................................................... 5

2.1.1 Strumenti di prototipizzazione .................................................................................................. 5

2.1.2 Framework per applicazioni cross-platform .............................................................................. 8

2.1.3 Ambienti di sviluppo ................................................................................................................ 11

2.2 Presentazione della soluzione ................................................................................................ 14

2.3 Struttura del documento ....................................................................................................... 15

3 MODELLO DELL’APPLICAZIONE ................................................................................ 16

3.1 Modello della View ............................................................................................................... 16

3.1.1 Struttura generale ................................................................................................................... 16

3.1.2 Modellazione astratta dei controlli utente .............................................................................. 19

3.1.3 Modellazione dei controlli con interazione unidirezionale con l’utente ................................. 25

3.1.4 Modellazione dei controlli multimediali .................................................................................. 27

3.1.5 Modellazione di controlli che permettono l’inserimento di informazioni .............................. 30

3.1.6 Modellazione di controlli con ruoli di navigazione .................................................................. 31

3.1.7 Modellazione di altri elementi................................................................................................. 36

3.2 Modello del Model ................................................................................................................ 38

3.2.1 Archiviazione tramite database relazionale ............................................................................ 39

3.2.2 Archiviazione tramite files ....................................................................................................... 40

3.2.3 Archiviazione tramite SharedPreferences ............................................................................... 41

3.2.4 Archiviazione tramite database cloud ..................................................................................... 41

3.3 Modello del Controller .......................................................................................................... 42

3.3.1 AdapterBinding ........................................................................................................................ 44

3.3.2 ModelConnector ...................................................................................................................... 46

4 PROTOCODE ...................................................................................................... 49

4.1 Introduzione ......................................................................................................................... 49

4.2 Software e framework utilizzati ............................................................................................. 52

III

4.2.1 ReactJS ..................................................................................................................................... 52

4.2.2 Yarn .......................................................................................................................................... 52

4.2.3 Redux e Redux-persist ............................................................................................................. 53

4.2.4 Bootstrap ................................................................................................................................. 53

4.2.5 Altre librerie ............................................................................................................................. 53

4.3 Architettura dell’applicazione ................................................................................................ 55

4.3.1 Architettura software .............................................................................................................. 55

4.3.2 Design dei dati ......................................................................................................................... 59

4.3.3 Connessione tra dati e vista .................................................................................................... 82

4.3.4 Design della navigazione ....................................................................................................... 101

4.3.5 Design dell’interfaccia utente ................................................................................................ 103

4.4 Output della prototipazione ................................................................................................ 106

4.5 Struttura del codice sorgente ............................................................................................... 106

5 MOBILECODEGENERATOR ................................................................................... 107

5.1 Introduzione ....................................................................................................................... 107

5.2 Software e framework utilizzati ........................................................................................... 107

5.2.1 Eclipse Modeling Framework ................................................................................................ 108

5.2.2 OpenArchitectureWare ......................................................................................................... 108

5.3 Struttura del generatore ...................................................................................................... 109

5.3.1 Struttura preesistente ........................................................................................................... 109

5.3.2 Design della generazione di applicazioni cross–platform ..................................................... 110

5.3.3 Generazione del codice ......................................................................................................... 116

6 VALUTAZIONE .................................................................................................. 125

6.1 Presentazione del caso di test .............................................................................................. 125

6.2 Produzione dell’applicazione ............................................................................................... 127

6.3 Analisi dell’applicazione generata ........................................................................................ 128

6.3.1 Analisi del tempo impiegato .................................................................................................. 128

6.3.2 Analisi quantitativa della generazione del codice ................................................................. 129

6.3.3 Analisi qualitativa del codice generato .................................................................................. 129

6.3.4 Limitazioni .............................................................................................................................. 130

7 CONCLUSIONI ................................................................................................... 132

7.1.1 Sviluppi futuri ........................................................................................................................ 133

8 BIBLIOGRAFIA ................................................................................................... 135

IV

Indice delle figure Figura 1.1 Ampiezza e penetrazione del mercato mobile nel mondo [1] ......................................................... 1

Figura 1.2 Crescita annuale del mercato mobile [1].......................................................................................... 1

Figura 1.3 Rapporto tra utenti e connessioni mobile [1] .................................................................................. 1

Figura 1.4 Guadagno del mercato mobile [1] .................................................................................................... 2

Figura 1.5 Vendite di smartphone per produttore in migliaia di unità nel secondo trimestre 2018 [2] .......... 2

Figura 1.6 Vendite di smartphone per sistema operativo in migliaia di unità nel secondo trimestre 2018 [2] 3

Figura 2.1 Interfaccia grafica di Proto.io ........................................................................................................... 5

Figura 2.2 Presentazione dell'interfaccia grafica di MarvelApp ........................................................................ 6

Figura 2.3 Interfaccia di Adobe Experience Design CC ...................................................................................... 7

Figura 2.4 Interfaccia grafica di DecoIDE ......................................................................................................... 11

Figura 3.1 Modello degli elementi di alto livello della componente View ...................................................... 17

Figura 3.2 Modello astratto dei controlli utente ............................................................................................. 19

Figura 3.3 Modello dei componenti con interazione unidirezionale con l’utente .......................................... 26

Figura 3.4 Modellazione dei controlli multimediali ......................................................................................... 28

Figura 3.5 Modellazione dei controlli con interazione bidirezionale con l'utente .......................................... 30

Figura 3.6 Modellazione dei controlli relativi alla navigazione ....................................................................... 32

Figura 3.7 Modellazione di elementi aggiuntivi della vista ............................................................................. 37

Figura 3.8 Modello della componente Model ................................................................................................. 39

Figura 3.9 Modellazione del Controller ........................................................................................................... 44

Figura 4.1 Schermata principale di Protocode ................................................................................................ 49

Figura 4.2 Schermata dedicata alle connessioni tra modello e vista .............................................................. 51

Figura 4.3 Esempio di output di Protocode ..................................................................................................... 51

Figura 4.4 Architettura di alto livello di Protocode ......................................................................................... 55

Figura 4.5 Navigazione di alto livello ............................................................................................................. 101

Figura 4.6 Navigazione dell'editor dedicato alla View .................................................................................. 101

Figura 4.7 Navigazione dell'editor dedicato al model ................................................................................... 102

Figura 4.8 Interfaccia utente per l'editor del modello .................................................................................. 103

Figura 4.9 Interfaccia utente per l'editor della View ..................................................................................... 104

Figura 4.10 Interfaccia utente per il scene model editor .............................................................................. 105

Figura 4.11 Organizzazione del codice di Protocode ..................................................................................... 106

Figura 5.1 Architettura di riferimento per l'applicazione generata .............................................................. 111

Figura 5.2 Utilizzo di un thunk nelle interazioni con la persistenza .............................................................. 116

Figura 5.3 Esempio di vincoli in un ViewController ....................................................................................... 120

Figura 5.4 Struttura di una scena di tipo SINGLE_VC .................................................................................... 122

Figura 5.5 Struttura di una scena di tipo SINGLE_VC_TAB ............................................................................ 122

Figura 5.6 Struttura di una scena di tipo SINGLE_VC .................................................................................... 122

Figura 6.1 Analisi del tempo impiegato per lo sviluppo dell'app di valutazione ........................................... 128

V

Indice delle tabelle Tabella 3.1 Campi di Application ..................................................................................................................... 17

Tabella 3.2 Campi di scene .............................................................................................................................. 18

Tabella 3.3 Campi di ViewController ............................................................................................................... 19

Tabella 3.4 Campi di UiControl ........................................................................................................................ 20

Tabella 3.5 Campi di UiPhoneControl .............................................................................................................. 21

Tabella 3.6 Campi di DimensionConstraint ..................................................................................................... 21

Tabella 3.7 Possibili combinazioni di vincoli di dimensione ............................................................................ 22

Tabella 3.8 Relazioni possibili tra elementi rilevanti ....................................................................................... 22

Tabella 3.9 Coesistenza di vincoli .................................................................................................................... 23

Tabella 3.10 Campi di PositionConstraint ....................................................................................................... 23

Tabella 3.11 Campi di ControlChain ................................................................................................................ 25

Tabella 3.12 Estensione di ViewController ai controlli con interazione unidirezionale .................................. 26

Tabella 3.13 Campi di TextView ...................................................................................................................... 27

Tabella 3.14 Campi di Map .............................................................................................................................. 27

Tabella 3.15 Campi di Card .............................................................................................................................. 27

Tabella 3.16 Estensione di ViewController ai controlli multimediali .............................................................. 28

Tabella 3.17 Campi di AudioPlayer .................................................................................................................. 29

Tabella 3.18 Campi di ImageView ................................................................................................................... 29

Tabella 3.19 Campi di PhotocameraController ............................................................................................... 29

Tabella 3.20 Campi di ImageView ................................................................................................................... 29

Tabella 3.21 Campi di VideocameraController ................................................................................................ 30

Tabella 3.22 Estensione di ViewController ai controlli con interazione bidirezionale con l’utente ............... 30

Tabella 3.23 Campi di EditText ........................................................................................................................ 31

Tabella 3.24 Estensione di ViewController ai controlli con ruolo nella navigazione ....................................... 33

Tabella 3.25 Estensione di ViewController ai controlli con ruolo nella navigazione ....................................... 33

Tabella 3.26 Campi di Navigation .................................................................................................................... 33

Tabella 3.27 Campi di Menu ............................................................................................................................ 34

Tabella 3.28 Campi di MenuItem .................................................................................................................... 34

Tabella 3.29 Campi di Button .......................................................................................................................... 34

Tabella 3.30 Campi di ListView ........................................................................................................................ 35

Tabella 3.31 Campi di ListViewCell .................................................................................................................. 35

Tabella 3.32 Campi di ListView ........................................................................................................................ 36

Tabella 3.33 Campi di ListViewCell .................................................................................................................. 36

Tabella 3.34 Estensione di ViewController agli elementi accessori ................................................................ 37

Tabella 3.35 Campi di ProgressDialog ............................................................................................................. 38

Tabella 3.36 Campi di AlertDialog ................................................................................................................... 38

Tabella 3.37 Campi di Entity ............................................................................................................................ 40

Tabella 3.38 Campi di EntityAttribute ............................................................................................................. 40

Tabella 3.39 Campi di EntityRelation ............................................................................................................... 40

Tabella 3.40 Campi di StorageRecord .............................................................................................................. 41

Tabella 3.41 Campi di PreferenceRecord ........................................................................................................ 41

Tabella 3.42 Campi di CloudObject ................................................................................................................. 42

Tabella 3.43 Campi di ObjectAttribute ............................................................................................................ 42

Tabella 3.44 Tipologie di AdapterBinding ........................................................................................................ 45

VI

Tabella 3.45 Campi di ModelConnector .......................................................................................................... 46

Tabella 3.46 Proprietà collegabili .................................................................................................................... 48

Tabella 4.1 Proprietà di "data handler object" ................................................................................................ 60

Tabella 4.2 Proprietà di oggetti di tipo PreferenceRecord .............................................................................. 61

Tabella 4.3 Proprietà di oggetti di tipo FileStorageRecord ............................................................................. 61

Tabella 4.4 Proprietà di oggetti di tipo Entity ................................................................................................. 62

Tabella 4.5 Proprietà di oggetti di tipo EntityAttribute ................................................................................... 62

Tabella 4.6 Proprietà di oggetti di tipo EntityRelation .................................................................................... 63

Tabella 4.7 Proprietà di oggetti di tipo CloudObject ....................................................................................... 63

Tabella 4.8 Proprietà di oggetti di tipo CloudObjectAttribute ........................................................................ 64

Tabella 4.9 Proprietà dell'oggetto Application ................................................................................................ 65

Tabella 4.10 Proprietà di oggetti di tipo Scene ............................................................................................... 65

Tabella 4.11 Proprietà di oggetti di tipo ViewController ................................................................................. 66

Tabella 4.12 Proprietà di oggetti di tipo ControlChain .................................................................................... 67

Tabella 4.13 Proprietà di oggetti di tipo MenuItem ........................................................................................ 68

Tabella 4.14 Proprietà di oggetti di tipo UiPhoneControl ............................................................................... 70

Tabella 4.15 Proprietà aggiuntive di controlli AudioRecorder ........................................................................ 71

Tabella 4.16 Proprietà aggiuntive di controlli Button ..................................................................................... 71

Tabella 4.17 Proprietà aggiuntive di controlli Card ......................................................................................... 72

Tabella 4.18 Proprietà aggiuntive di controlli EditText ................................................................................... 72

Tabella 4.19 Proprietà aggiuntive di controlli GridView.................................................................................. 72

Tabella 4.20 Proprietà aggiuntive di controlli Label ........................................................................................ 72

Tabella 4.21 Proprietà aggiuntive di controlli ListView ................................................................................... 72

Tabella 4.22 Proprietà aggiuntive di controlli Map ......................................................................................... 72

Tabella 4.23 Proprietà aggiuntive di controlli PhotocameraController .......................................................... 72

Tabella 4.24 Proprietà aggiuntive di controlli VideocameraController ........................................................... 72

Tabella 4.25 Proprietà aggiuntive di controlli WebView ................................................................................. 73

Tabella 4.26 Proprietà aggiuntive di controlli Container ................................................................................ 73

Tabella 4.27 Proprietà di oggetti di tipo SourceType ...................................................................................... 74

Tabella 4.28 Proprietà di oggetti di tipo GridViewCell .................................................................................... 74

Tabella 4.29 Proprietà di oggetti di tipo ListViewCell ..................................................................................... 75

Tabella 4.30 Proprietà di oggetti di tipo Constraint ........................................................................................ 76

Tabella 4.31 Proprietà di oggetti di tipo AlertDialog ....................................................................................... 77

Tabella 4.32 Proprietà di oggetti di tipo ProgressDialog ................................................................................. 77

Tabella 4.33 Proprietà di oggetti di tipo AsyncTask ........................................................................................ 78

Tabella 4.34 Proprietà di oggetti di tipo Navigation ....................................................................................... 79

Tabella 4.35 Proprietà di oggetti di tipo AdapterBinding ................................................................................ 80

Tabella 4.36 Proprietà di oggetti di tipo ModelConnector.............................................................................. 81

Tabella 4.37 Payload dell'azione CreatePreferenceRecord ............................................................................. 83

Tabella 4.38 Payload dell'azione DeletePreferenceRecord ............................................................................. 84

Tabella 4.39 Payload dell'azione CreateFileStorageRecord ............................................................................ 84

Tabella 4.40 Payload dell'azione DeleteFileStorageRecord ............................................................................ 84

Tabella 4.41 Payload dell'azione CreateEntity ................................................................................................ 84

Tabella 4.42 Payload dell'azione EditEntity ..................................................................................................... 85

Tabella 4.43 Payload dell'azione DeleteEntity ................................................................................................ 85

Tabella 4.44 Payload dell'azione CreateEntityAttribute .................................................................................. 85

VII

Tabella 4.45 Payload dell'azione DeleteEntityAttribute .................................................................................. 85

Tabella 4.46 Payload dell'azione CreateEntityRelation ................................................................................... 85

Tabella 4.47 Payload dell'azione DeleteEntityRelation ................................................................................... 86

Tabella 4.48 Payload dell'azione CreateCloudObject ...................................................................................... 86

Tabella 4.49 Payload dell'azione EditCloudObject .......................................................................................... 86

Tabella 4.50 Payload dell'azione DeleteCloudObject ...................................................................................... 86

Tabella 4.51 Payload dell'azione CreateCloudObjectAttribute ....................................................................... 87

Tabella 4.52 Payload dell'azione DeleteCloudObjectAttribute ....................................................................... 87

Tabella 4.53 Payload dell'azione CreateScene ................................................................................................ 87

Tabella 4.54 Payload dell'azione EditScene ..................................................................................................... 87

Tabella 4.55 Payload dell'azione DeleteScene ................................................................................................ 87

Tabella 4.56 Payload dell'azione LinkViewController ...................................................................................... 88

Tabella 4.57 Payload dell'azione UnlinkViewController .................................................................................. 88

Tabella 4.58 Payload dell'azione EditContainer .............................................................................................. 88

Tabella 4.59 Payload dell'azione CreateViewController ................................................................................. 88

Tabella 4.60 Payload dell'azione EditViewController ...................................................................................... 89

Tabella 4.61 Payload dell'azione DeleteViewController.................................................................................. 89

Tabella 4.62 Payload dell'azione CreateUiPhoneControl ................................................................................ 89

Tabella 4.63 Payload dell'azione EditUiPhoneControl .................................................................................... 89

Tabella 4.64 Payload dell'azione DeleteUiPhoneControl ................................................................................ 90

Tabella 4.65 Payload dell'azione CreateControlChain ..................................................................................... 91

Tabella 4.66 Payload dell'azione EditControlChain ......................................................................................... 91

Tabella 4.67 Payload dell'azione DeleteControlChain ..................................................................................... 91

Tabella 4.68 Payload dell'azione CreateConstraint ......................................................................................... 91

Tabella 4.69 Payload dell'azione EditConstraint ............................................................................................. 92

Tabella 4.70 Payload dell'azione DeleteConstraint ......................................................................................... 92

Tabella 4.71 Payload dell'azione CreateMenuItem ......................................................................................... 92

Tabella 4.72 Payload dell'azione EditMenuItem ............................................................................................. 93

Tabella 4.73 Payload dell'azione DeleteMenuItem ......................................................................................... 93

Tabella 4.74 Payload dell'azione CreateGridViewCell ..................................................................................... 93

Tabella 4.75 Payload dell'azione EditGridViewCell ......................................................................................... 93

Tabella 4.76 Payload dell'azione DeleteGridViewCell ..................................................................................... 93

Tabella 4.77 Payload dell'azione CreateListViewCell ...................................................................................... 94

Tabella 4.78 Payload dell'azione EditListViewCell ........................................................................................... 94

Tabella 4.79 Payload dell'azione DeleteListViewCell ...................................................................................... 94

Tabella 4.80 Payload dell'azione CreateNavigation ........................................................................................ 94

Tabella 4.81 Payload dell'azione EditNavigation ............................................................................................. 95

Tabella 4.82 Payload dell'azione DeleteNavigation ........................................................................................ 95

Tabella 4.83 Payload dell'azione EditSourceType ........................................................................................... 95

Tabella 4.84 Payload dell'azione CreateAlertDialog ........................................................................................ 96

Tabella 4.85 Payload dell'azione EditAlertDialog ............................................................................................ 96

Tabella 4.86 Payload dell'azione DeleteAlertDialog ........................................................................................ 96

Tabella 4.87 Payload dell'azione CreateProgressDialog .................................................................................. 96

Tabella 4.88 Payload dell'azione EditProgressDialog ...................................................................................... 97

Tabella 4.89 Payload dell'azione DeleteProgressDialog .................................................................................. 97

Tabella 4.90 Payload dell'azione CreateAsyncTask ......................................................................................... 97

VIII

Tabella 4.91 Payload dell'azione EditAsyncTask .............................................................................................. 97

Tabella 4.92 Payload dell'azione DeleteAsyncTask ......................................................................................... 97

Tabella 4.93 Payload dell'azione ChangeName ............................................................................................... 98

Tabella 4.94 Payload dell'azione ChangeCompanyIdentifier .......................................................................... 98

Tabella 4.95 Payload dell'azione CreateAdapterBinding................................................................................. 98

Tabella 4.96 Payload dell'azione DeleteAdapterBinding ................................................................................. 99

Tabella 4.97 Payload dell'azione CreateModelConnector .............................................................................. 99

Tabella 4.98 Payload dell'azione DeleteModelConnector ............................................................................ 100

Tabella 6.1 Dimensione della codebase dell'app di valutazione ................................................................... 129

IX

Sommario

Il mondo di oggi è sempre più connesso, veloce, pervaso dalla tecnologia e dai servizi che questa può offrire

e sempre in cerca di novità. Oltre alla pervasione, l’informatica di oggi affronta il problema della velocità del

cambiamento. La risposta da parte degli sviluppatori di applicazioni mobili è sempre più orientata allo

sviluppo di strumenti capaci di supportare la variabilità e la velocità del mercato, permettendo di realizzare

applicazioni sempre più scalabili, più adattabili, ricche di funzionalità ed accattivanti nella grafica, e

prodotte e immesse sul mercato nel minor tempo possibile. La tendenza degli ultimi anni è quella di creare

strumenti a supporto dello sviluppo di applicazioni che possano adattarsi non solo a dispositivi diversi per

dimensioni o per aspetto, ma anche per sistema operativo. Sono esempi di questa tendenza i framework

Xamarin, ReactNative, Titanium e Cordova. Questo lavoro mira a fornire strumenti che consentano di

andare oltre e di abbattere ulteriormente il tempo necessario a trasformare un’idea in un’app disponibile

sul mercato. A questo scopo sono stati sviluppati due strumenti per assistere gli sviluppatori: Protocode e

MobileCodeGenerator. Protocode è uno strumento dedicato a dare forma all’idea: esattamente come uno

strumento di prototipizzazione, esso consente di modellare graficamente i diversi aspetti dell’applicazione,

dalle viste ai dati richiesti, fino alla logica che li collega. La forma che Protocode mira a dare all’idea non è

tanto quella del prototipo, ma quella del modello strutturato. L’adozione del modello strutturato consente,

infatti, di generare automaticamente il codice dell’applicazione tramite lo strumento

MobileCodeGenerator. La generazione può essere fatta per diversi linguaggi, da Java per Android a Swift

per iOS. In questo lavoro di tesi si valuterà l’efficacia di questa toolchain applicata alla generazione di codice

cross - platform, in modo da fondere i benefici di una generazione automatica e con quelli di uno sviluppo

non specifico rispetto alla piattaforma software di destinazione, senza perdere di vista l’aspetto delle

performance.

X

Abstract

Today, the world is getting more and more connected, fast, pervaded by the technology and in search of

continuous innovation. Aside pervasion, the computer science today lives and faces up to the problem of

velocity of change. Mobile application developers try to face this situation creating tools able to support

variability and velocity of the market, allowing them to realize applications that are more scalable, more

adaptable, richer in functionality and more attractive from a graphical point of view, and produced and

released in the least possible time. Over the recent years, the trend is to create tools supporting the

development of applications capable to adapt not only to different screen sizes or form factors, but also to

different operative systems. Examples of this trend are the Xamarin framework, the ReactNative library, the

Titanium software development kit and the Apache Cordova solution. This master thesis aims at providing

tools to go beyond this point, further reducing the time necessary to transform an idea into a market ready

mobile application. In order to achieve this, two tools have been developed: Protocode and

MobileCodeGenerator. The former is a tool aimed at shaping an idea: just like a prototyping tool, it allows

to model in a graphical fashion the different aspects of a mobile application, from views to required data,

up to the logic that connects them. The shape given by Protocode to the idea is not that of a prototype, but

of a structured model. The adoption of the model, in effect, enables the automatic generation the source

code of the application using the MobileCodeGenerator tool. The conversion can target different languages,

from Java for Android to Swift for iOS. This thesis work investigates the effectiveness of this toolchain

applied to the generation of cross platform source code, in such a way to blend the benefits of automatic

generation with a development flow, which is not specific to the target software platform, without sacrifice

on performance

1

1 Introduzione La rivoluzione portata dall’avvento dei dispositivi mobili è oggi un fenomeno globale in continua crescita. I

dati del 2019 (Figura 1.1) evidenziano 5 miliardi di utenti mobili, con una penetrazione di mercato del 67%,

e un trend di crescita del 2% (Figura 1.2) nell’ultimo anno, come provato dalle ricerche di We Are Social [1].

Figura 1.1 Ampiezza e penetrazione del mercato mobile nel mondo [1]

Figura 1.2 Crescita annuale del mercato mobile [1]

Tale crescita, oltre che dalla rapidità e dalla globalità, è caratterizzata da una forte variabilità per quanto

riguarda i dispositivi. Il mercato spazia da smartwatch a smartphone e tablet, da televisori connessi ad

automobili. Questo spiega come sia possibile che il numero di connessioni mobili abbia ormai superato la

popolazione mondiale (Figura 1.3).

Figura 1.3 Rapporto tra utenti e connessioni mobile [1]

2

Guardando solo al mercato mobile di smartphone, smartwatch e tablet, si comprende facilmente

l’interesse di aziende produttrici di software per le applicazioni mobili, capaci di produrre in un anno incassi

per quasi 20 miliardi di dollari solamente sugli store ufficiali (Figura 1.4). Grazie all’avanzamento

tecnologico dei dispositivi mobili, che utilizzano processori sempre più performanti e che sono equipaggiati

con un crescente numero di sensori, l’attrazione del mercato mobile è sicuramente molto interessante per

qualsiasi compagnia del settore.

Figura 1.4 Guadagno del mercato mobile [1]

Esaminando tale mercato, si nota immediatamente un contrasto tra il mondo dell’hardware e quello del

software. Il mercato dei produttori hardware appare piuttosto vario, con colossi come Samsung, Apple e

Huawei capaci di detenere il 44.5% del mercato, affiancati da realtà in rapidissima crescita, prima fra tutte

Xiaomi che ne secondo trimestre del 2018 incrementa del 3% la propria quota di mercato.

Figura 1.5 Vendite di smartphone per produttore in migliaia di unità nel secondo trimestre 2018 [2]

Tale frammentazione si oppone al consolidamento, per quanto riguarda il mercato dei sistemi operativi, di

Android e iOS, che detengono il 99.9% delle quote di mercato. Con l’annuncio, dato a inizio 2018, della

cessazione del supporto ufficiale per Windows Phone, sul mercato restano davvero solo i due colossi di

Cupertino e Mountain View. Il dominio di Android è sempre indiscusso, calcolando anche il sorpasso di

Huawei (i cui device montano sistemi operativi Android) ai danni di Apple nel secondo trimestre del 2018.

3

Figura 1.6 Vendite di smartphone per sistema operativo in migliaia di unità nel secondo trimestre 2018 [2]

Il successo di tali sistemi operativi, oltre alla stabilità e alla quantità di funzionalità offerte, sta nella

ricchezza dei rispettivi application store: GooglePlay e AppStore. Le applicazioni mobili sono, quindi, un

elemento portante del mercato mobile, in cui rappresentano, al contempo, una grande sfida e una grande

possibilità di business.

Per il mercato delle applicazioni mobili una delle sfide chiave sta nel fronteggiare la dualità di iOS e Android,

data soprattutto la loro profonda diversità. Un approccio tradizionale prevedrebbe di portare avanti due

linee separate di sviluppo della stessa applicazione, la prima per Android e la seconda per iOS, con

conseguente aumento dei costi, in particolare di manutenzione, e delle competenze richieste. La risposta

dal mondo degli sviluppatori si concretizza nello sviluppo e nel rilascio di strumenti per lo sviluppo cross –

platform che consentano, appunto, di portare avanti una sola linea di sviluppo e manutenzione ottenendo

un risultato che possa essere installato su entrambe le piattaforme, con conseguente risparmio sui costi,

riduzione dello spettro di competenze richiesto e contrazione dei tempi di sviluppo. Proprio i tempi, infatti,

costituiscono la seconda grande challenge del mercato del software moderno: la rapidità dello sviluppo,

l’anticipazione del rilascio e l’agilità del processo di produzione sono elementi chiave per il successo di

qualsiasi software, in particolare per le applicazioni mobili, che sono caratterizzate da un’estrema facilità di

acquisizione e installazione così come di rimozione. L’engagement dell’utente procede a pari importanza

con la reattività dello sviluppatore nel produrre aggiornamenti, nuove features e risoluzione di problemi.

Questo lavoro di tesi ambisce a presentare approcci e strumenti per favorire lo sviluppo rapido, efficace e

di qualità di applicazioni mobili cross-platform. In particolare, si andrà a presentare una soluzione per la

generazione automatica del codice sorgente di applicazioni mobili cross-platform a partire dalla struttura

dell’applicazione stessa, descritta nella fase di prototipizzazione. L’attività di prototipizzazione, infatti,

utilizza già una descrizione dell’applicazione per la creazione di un prototipo interattivo. In questo lavoro di

tesi si cercherà di definire una modalità per sfruttare al meglio tale momento in modo da ottenere, oltre al

prototipo funzionante, anche una rappresentazione strutturata dei dati utilizzati per la creazione del

prototipo stesso, che costituirà un modello dell’applicazione. Un prototipo tradizionalmente guarda

soltanto alla componente visiva di un’applicazione, mentre in questo lavoro di tesi ne verrà considerata una

variante arricchita della descrizione delle componenti Model e Controller, in modo da ottenere un modello

più ricco, completo e utile. È intuitivo, infatti, considerare che la completezza del modello è direttamente

proporzionale alla quantità di codice che può essere generato a partire da esso rispetto al codice totale

richiesto. All’attività di prototipizzazione segue, quindi, una generazione automatica del codice

dell’applicazione a partire dal modello creato insieme al prototipo. I due momenti di prototipizzazione e di

generazione del codice sono associati ai due tools presentati in questo lavoro di tesi: Protocode e

MobileCodeGenerator. Il primo è uno strumento di prototipizzazione orientato alla produzione di un

4

modello dell’applicazione, mentre il secondo è un generatore di codice basato su un modello

dell’applicazione da generare. Entrambi gli strumenti non sono interamente frutto di questo lavoro di tesi,

e raggiungono ora la versione 5.0. La prima versione di Mobile Code Generator è nata nel 2012 dal lavoro di

Gregorio Perego e Stefania Pezzetti [3], ed è stata successivamente migliorata da Mattia Natali [4] nel 2013

che ha inoltre introdotto lo strumento Protocode. Nell’A.A. 2015/2016 Aldo Pintus [5] ha aggiunto la

possibilità di modellare l’interfaccia per la componente smartwatch, mentre nell’A.A. 2017/2018 Alessio

Rossotti [6] ha introdotto la modellazione e la generazione del codice relativi alla parte Model. In questo

lavoro di tesi è stato aggiunto in primo luogo il supporto alla generazione di codice cross-platform basato

sul framework ReactNative. Dal punto di vista delle funzionalità supportate, questa generazione è

equivalente alla generazione nativa implementata nei precedenti lavori, tranne per la possibilità di gestire

attività in background, che non è disponibile a causa di una limitazione del framework. In secondo luogo è

stata introdotta la possibilità di modellare parte della componente Controller, soprattutto per quanto

riguarda la gestione di eventi provenienti dalla View. Una modellazione completa del Controller infatti

sarebbe eccessivamente complessa per lo scopo di questo lavoro di tesi, volto a utilizzare la fase di

prototipizzazione. Il dettaglio di quali elementi del Controller sia possibile modellare, e quindi generare, è

riportato nel capitolo 3.3. La generazione di codice corrispondente al modello del Controller è stata

implementata per la sola piattaforma ReactNative, ma può essere facilmente estesa al codice nativo.

Nei prossimi capitoli verrà presentata, dopo una valutazione dello stato dell’arte, la struttura del modello di

riferimento, sia per quanto riguarda il lavoro preesistente sia per quanto riguarda invece le novità

introdotte da questo lavoro di tesi, in modo da delineare un quadro completo delle capacità espressive

implementate. Verranno poi presentati i due strumenti Protocode e MobileCodeGenerator, e ne verrà

valutata l’efficacia rispetto alla quantità e alla qualità del codice generato.

5

2 Contesto generale

2.1 Stato dell’arte In questa sezione vengono discussi gli strumenti, i framework e i software attualmente utilizzati nella

prototipizzazione e nella produzione di applicazioni mobili. Tali strumenti affiancano gli sviluppatori sia

durante le attività di design che durante le attività di implementazione vera e propria. Vi sono poi strumenti

ibridi, che consentono lo sviluppo intelligente dell’applicazione affiancato da una live preview del risultato

finale.

2.1.1 Strumenti di prototipizzazione

Le applicazioni di prototipizzazione sono strumenti che permettono, attraverso editor grafici e senza la

necessità di scrivere alcun tipo di codice eseguibile, di disegnare l’interfaccia utente dell’applicazione che si

vuole sviluppare. Essi sono molto utili al fine di avere un’anteprima visiva di quella che sarà la GUI e

valutarne le possibili alternative, prima di iniziare la fase di sviluppo in codice vera e propria. In tal senso,

costituiscono un eccellente strumento di dialogo tra il team di design e gli stakeholders. Come verrà

evidenziato nei paragrafi successivi, la criticità maggiore risiede nel loro utilizzo da parte del team che cura

l’implementazione dell’applicazione.

Proto.io

Proto.io [7] è uno strumento di realizzazione di prototipi realistici di applicazione mobili capaci di adattarsi

a varie tipologie di dispositivi, tra cui anche smartTV, console da gaming e smartwatch. L’utilizzo è semplice

e intuitivo, basato sul drag and drop di componenti scelti da una ricca palette laterale. Sono disponibili

anche componenti specifici di una singola piattaforma software e funzionalità più avanzate come la

modellazione di diversi stati per una schermata, la gestione delle transizioni o l’assegnazione di azioni a

elementi della vista. È infine possibile importare prototipi sviluppati con altri tool, quali Sketch [8] e

Photoshop [9].

Figura 2.1 Interfaccia grafica di Proto.io

6

Alla ricchezza di features offerte corrisponde costo piuttosto elevato: il piano più economico è offerto a

29$/mese, ma limita ad un solo utente e 5 progetti. Per confronto, il piano Corporate offre 10 utenti e 30

progetti attivi al costo di 199$/mese.

Oltre al costo molto elevato, Proto.io presenta un’altra limitazione importante. Il prototipo che è possibile

ottenere con tale tool è un prodotto che può essere utilizzato a fini di specifica dell’applicazione. Esso,

infatti, può essere utilizzato quasi unicamente come strumento di confronto con gli stakeholders

relativamente all’aspetto grafico e alla navigazione all’interno dell’applicazione, e come target dell’attività

di implementazione. Tuttavia, il prototipo contiene molte informazioni riguardo alla struttura

dell’applicazione, che possono essere utilizzate per la generazione automatica almeno di uno scheletro

dell’applicazione durante la fase di implementazione, in modo da ridurre i tempi di sviluppo sfruttando il

lavoro fatto in fase di prototipizzazione.

MarvelApp

MarvelApp [10] è uno strumento di prototipizzazione caratterizzato dalla possibilità di creare prototipi

realistici sia in termini di aspetto grafico dell’applicazione sia in termini di interazione con l’utente: infatti è

possibile realizzare prototipi arricchiti con azioni e transizioni tra scene. Il punto di forza di MarvelApp è

l’attenzione al lavoro in team: questo tool offre un sistema di commenti e chat legato agli strumenti di

prototipizzazione, in modo che il team possa discutere in merito ad un elemento del prototipo legando la

chat all’elemento stesso. Da ultimo, MarvelApp offre la possibilità di importare modelli e prototipi da

Sketch [8].

Figura 2.2 Presentazione dell'interfaccia grafica di MarvelApp

Il prodotto viene offerto in un range di piani di acquisto molto variabili per prezzo e funzionalità. Si passa

dal piano base, gratuito ma limitato ad un solo utente e un solo progetto, ad un piano sempre basilare ma

al costo di 16$/mese che consente un numero illimitato di progetti, la possibilità di scaricare i prototipi e la

rimozione del logo Marvel, ad un piano Enterprise che offre workspaces, 99.9% uptime, Single Sign-On e

molte altre features aggiuntive, ad un costo da definire tramite contatto con l’azienda.

7

In modo simile a Proto.io [7], questo strumento separa ancora nettamente le attività di prototipizzazione

ed implementazione dell’applicazione. Per quanto MarvelApp si avvicini ai produttori di applicazioni,

fornendo funzionalità di supporto al lavoro in team, essa si rivolge solamente ai team di designer che

studiano l’aspetto grafico, l’organizzazione delle viste e la navigazione tra le stesse, e manca di un supporto

al team di implementazione. L’informazione che viene inserita nel prototipo nasce e muore con esso, e non

viene sfruttata appieno. Analogamente, il team che si occupa di implementare l’applicazione parte da una

pagina bianca, su cui va a riscrivere, questa volta come codice eseguibile, tutte le informazioni già utilizzate

dai designer nella produzione del prototipo. Tale approccio non è ideale in quanto è necessario lavorare

due volte con le stesse informazioni, dato che la prototipizzazione e l’implementazione non dispongono di

punti di contatto.

Adobe Experience Design CC

Adobe Experience Design CC [11] è il prodotto di Adobe disegnato appositamente per il design e la

creazione di prototipi di applicazioni mobili. Il punto di forza di questo strumento è la ricchezza di features.

Esso offre la possibilità di utilizzare strumenti rapidi ed intuitivi per il design e di arricchire i prototipi con

comandi vocali e altre esperienze beyond the screen. Inoltre, consente di adattare automaticamente

un’interfaccia a schermi di diverse forme e dimensioni, e di creare animazioni tra le diverse schermate.

Infine, utilizzando l’app mobile è possibile osservare il risultato della prototipizzazione direttamente sul

proprio device, e di interagirvi come farebbe un utente finale.

Figura 2.3 Interfaccia di Adobe Experience Design CC

Questo prodotto è disponibile con licenza gratuita limitata ad un singolo prototipo condiviso, mentre sono

disponibili i piani “Single App” e “Creative Cloud All Apps” che, oltre a rimuovere tale limitazione, offrono

l’accesso alla libreria di font di Adobe, alle Creative Cloud Libraries e a 100GB di spazio di archiviazione

cloud.

Questo prodotto è probabilmente la miglior soluzione sul mercato per la prototipizzazione di applicazioni

mobili, e possiede una ricchezza di features ineguagliata in tutti gli altri tool. Inoltre la possibilità di avere un

prototipo interattivo funzionante sul proprio device è certamente molto interessante, ma non viene

sfruttata come potrebbe. Un prototipo eseguibile su un device potrebbe, infatti, essere utilizzato come

punto di partenza per l’attività di implementazione vera e propria, in quanto contiene già tutte le

informazioni sui layout, sulle schermate, sulla navigazione e sull’interazione con l’utente. Peraltro, il fatto

che il prototipo sia eseguibile suggerisce che esso sia memorizzato in qualche formato strutturato che

potrebbe essere utilizzato come punto di partenza per la generazione di codice nativo, o come linguaggio

8

ad alto livello per la descrizione di applicazioni che poi vengono eseguite da una apposita macchina virtuale

in grado di operare su tale linguaggio. Sebbene appaia molto vicina, tale funzionalità non è disponibile in

Adobe Experience Design CC, e ciò agisce da barriera al flusso di dati tra design e prototipizzazione da un

lato e implementazione dall’altro.

2.1.2 Framework per applicazioni cross-platform

La necessità di produrre la medesima applicazione per due sistemi operativi radicalmente diversi ha portato

la community di sviluppatori a pensare soluzioni che permettessero di utilizzare la stessa codebase. Tali

soluzioni agiscono essenzialmente in base a due approcci. Il primo consiste nell’introdurre un layer di

astrazione dal sistema operativo, mentre il secondo agisce sul processo di compilazione inserendo passaggi

di cross-compilazione. In questa sezione saranno presentati i framework più popolari per lo sviluppo di

applicazioni cross-platform.

Xamarin

Xamarin [12] è la soluzione di Microsoft [13] per lo sviluppo di applicazioni cross-platform. Ideato da

un’azienda indipendente, Xamarin è ora integrato nella suite Visual Studio [14].

Xamarin si basa sul linguaggio C# per la definizione della logica applicativa e su linguaggio XAML per la

definizione delle interfacce. Inoltre, il framework mette a disposizione binding a due vie per connettere le

viste con i dati. Infine, è possibile utilizzare le funzioni della libreria .NET. L’interazione con i servizi del

sistema operativo è ottenuta tramite il linker. Xamarin, infatti, per ogni release di Android o iOS, genera

una libreria di definizioni che riflette l’interfaccia esposta dal sistema operativo ad un’app. In tal modo è

possibile utilizzare le funzioni offerte dal sistema operativo nel codice C#, compilare e poi collegare la vera

funzione nativa tramite il processo di linking.

Lo sviluppo cross – platform è ottenuto appunto agendo sul processo di compilazione: per i dispositivi che

eseguono iOS il codice C# viene compilato ahead of time, la libreria .NET viene inclusa dal linker e le parti

non necessarie vengono eliminate. Ciò è necessario data la limitazione di Apple sulla generazione di codice

a runtime sui device che eseguono iOS. Su Android, invece, il codice viene convertito in un linguaggio

intermedio che viene poi viene eseguito tramite just in time compilation sulla MonoVM, che viene attivata

in parallelo ad ART.

Su Android l’installazione di un’app Xamarin ha un costo notevole in termini di memoria richiesta, dato che

devono essere installati anche la MonoVM compatibile con il sistema operativo in uso e alcuni pacchetti di

helpers.

La principale limitazione di questo approccio è la forte dipendenza dal processo di compilazione. Essa è,

infatti, lunga e complessa e, nonostante Xamarin sia ufficialmente supportato e sviluppato da Microsoft,

molto incline a presentare problemi. Inoltre, questo rende impossibile il live editing, ovvero la possibilità di

modificare il codice sorgente e vedere in tempo reale i cambiamenti prodotti dalla modifica nel simulatore

o anche nel proprio dispositivo.

Infine, va sottolineato che Xamarin non è una soluzione completa per lo sviluppo cross-platform. Sebbene

essa fornisca la possibilità di codificare l’app utilizzando interamente il linguaggio C#, è anche vero che una

soluzione Xamarin si compone di minimo tre progetti: un progetto condiviso, un progetto specifico per

Android e un progetto specifico per iOS. Infatti, molte parti dell’applicazione vanno progettate e

implementate indipendentemente per Android e per iOS, date le profonde differenze tra i due sistemi

operativi. L’orchestrazione dei tre progetti avviene nel seguente modo: nel progetto condiviso viene

9

definita l’interfaccia del servizio che richiede codice specifico Android o iOS. Tale interfaccia viene poi

implementata nei progetti specifici delle varie piattaforme. Nel progetto condiviso il servizio viene

istanziato tramite l’utilizzo di un servizio apposito, il Dependency Service, che prende come input

l’interfaccia definita precedentemente. In fase di compilazione, dato il sistema operativo di destinazione,

viene incluso il codice corretto e le chiamate al Dependency Service vengono risolte.

PhoneGap

PhoneGap [15] è la soluzione supportata da Adobe [16] per lo sviluppo di applicazioni cross-platform, ed è

basato sul tool Cordova di proprietà di Apache [17].

PhoneGap ottiene uno sviluppo cross-platform attraverso l’utilizzo di strumenti web e di un layer di

astrazione.

L’applicazione da produrre viene sviluppata a tutti gli effetti come una applicazione web, utilizzando quindi

il linguaggio HTML per la definizione delle interfacce, il CSS per l’applicazione di stili e infine il linguaggio

JavaScript per la definizione della logica applicativa. JavaScript, oltre alla classica libreria di funzioni

utilizzate per la manipolazione del DOM, viene arricchito con l’aggiunta di una libreria di funzioni che

consentono la comunicazione con i servizi offerti dal sistema operativo del device.

Il layer di astrazione consiste nell’inserire la web app in un componente di tipo WebView, disponibile su

tutte le piattaforme. Tale componente viene esteso in modo da fornire al JavaScript eseguito al suo interno

le funzioni per interagire con il sistema operativo. Tali funzioni sono, infatti, implementate nel layer di

astrazione con codice nativo, e devono poter essere richiamate dalla logica applicativa.

La limitazione maggiore di questo approccio consiste, appunto, nel layer di astrazione e nell’utilizzo di un

linguaggio interpretato come Javascript. Questo si traduce in un problema di performance

dell’applicazione. Su iOS le applicazioni native eseguono direttamente codice macchina, che è

ineguagliabile in termini di performance. Su Android, con l’introduzione di ART, le applicazioni vengono

rilasciate come codice di livello bytecode, che viene poi compilato ahead-of-time al momento

dell’installazione dell’app stessa, ottenendo quindi performance native. È facilmente intuibile che le

performance del JavaScript sono nettamente inferiori. Inoltre, l’aspetto grafico dell’applicazione è gestito

dall’HTML e non da controlli nativi. Questo introduce un altro motivo di perdita a livello di performance.

AppceleratorTitanium

Titanium è un Software Development Kit open source prodotto dall’azienda Appcelerator [18].

Titanium ottiene uno sviluppo cross-platform attraverso l’utilizzo del linguaggio JavaScript per la definizione

della logica applicativa. Differentemente da PhoneGap, le interfacce utente non sono basate su HTML ma

su componenti nativi, disegnati su richiesta della logica applicativa.

L’SDK di Titanium espone una API costituita da oggetti proxy che collegano in modo bidirezionale il sistema

operativo e i servizi nativi da un lato e la logica JavaScript dall’altro. In questo modo, le chiamate JavaScript

a servizi nativi vengono mappate in chiamate native a tutti gli effetti, e gli eventi lanciati dai servizi nativi

possono essere catturati e gestiti tramite JavaScript. Questo meccanismo permette, infine, di unificare i

punti comuni delle interfacce esposte dai due sistemi operativi Android e iOS sotto un’unica interfaccia,

senza comunque rinunciare a funzionalità esposte da una sola delle due piattaforme. Infatti, i punti comuni

vengono unificati, e tutti gli elementi non comuni restano accessibili.

10

Il progetto prevede anche un IDE dedicato chiamato Titanium Studio, che è dotato di un debugger integrato

che permette di provare l’applicazione sia sul simulatore sia sul dispositivo fisico, indispensabile nel caso di

sviluppo su Android (dato che l’emulatore di Google non è molto performante). TitaniumStudio include

anche strumenti per la definizione grafica delle interfacce dell’applicazione e strumenti per equipaggiare

l’applicazione con funzionalità di analitica ed error detection, i cui dati sono visualizzabili tramite apposita

dashboard.

Questo approccio presenta tre limitazioni principali. La prima, più lieve, riguarda le performance di

esecuzione. Per quanto il codice sfrutti chiamate a servizi nativi, la logica è comunque eseguita in

JavaScript, le cui performance di esecuzione sono notoriamente non ottimali. Inoltre, vi è un piccolo

overhead dovuto all’utilizzo dei Proxy. La seconda limitazione sta nel fatto che l’API esposta dall’SDK al

programmatore è vasta e necessita di studio apposito. La terza e ultima limitazione consiste nel fatto che gli

strumenti per la definizione grafica delle interfacce non possono essere utilizzati a scopi di

prototipizzazione, in quando costituiscono solo un aiuto alla definizione del codice, e non uno strumento

utilizzabile dal team di design.

React Native

ReactNative [19] è la soluzione supportata da Facebook per la produzione di applicazioni cross-platform.

Tale soluzione si configura come un’estensione al mondo mobile della libreria ReactJS.

ReactNative ottiene lo sviluppo cross-platform tramite l’utilizzo di una estensione del linguaggio JavaScript,

chiamata JSX. JSX nasce nel contesto di ReactJS al fine di semplificare la definizione di componenti

includendovi il frammento di albero DOM incaricato di rendere il componente stesso. ReactNative adotta lo

stesso principio, ma sostituisce i tag HTML disponibili in ReactJS con tag mappati su controlli nativi.

I veri punti di forza di ReactNative non risiedono, tuttavia, nell’utilizzo del JSX, che è solo uno strumento.

ReactNative si basa su tre concetti essenziali: sintassi dichiarativa, composizione di componenti e flusso

unidirezionale dei dati. Questo framework, infatti, si basa sulla definizione di componenti incapsulati, che

gestiscono ciascuno il proprio stato, e sulla composizione degli stessi a formare interfacce complesse. Dato

che la logica è scritta in JavaScript e non è basata su templates, è possibile passare tra vari componenti dati

strutturati. Il flusso dei dati è obbligatoriamente unidirezionale dal componente padre ai componenti figli.

La comunicazione tra figli e padre è possibile solo se il padre passa ai figli stessi, tra i vari dati, anche alcune

funzioni, che il figlio può invocare quando ha necessità di comunicare con il padre. Tale meccanismo

permette di implementare la gestione di eventi, che si muovono quindi dalle foglie alla radice dell’albero

dei componenti, in direzione opposta ai dati. Questa struttura dichiarativa, unita al flusso unidirezionale dei

dati, rende il codice molto più leggibile, meno incline a difetti e semplifica la risoluzione dei bug. I

componenti che compongono le interfacce, pur essendo gestiti ed orchestrati tramite logica JavaScript,

sono componenti nativi a tutti gli effetti. Il collegamento tra il dominio nativo e il dominio JavaScript è a

carico del framework. Infine, l’API esposta da ReactNative ad uno sviluppatore è disegnata con un criterio di

minimalismo, in modo da ridurre notevolmente i tempi di apprendimento, ed è fortemente ispirata al

largamente conosciuto mondo web. L’integrazione con ExpoKit permette anche il live editing del codice con

anteprima direttamente sul proprio device.

La limitatezza dell’API esposta da ReactNative è anche un punto di debolezza del framework stesso. Infatti,

le applicazioni devono integrare un numero estremamente elevato di plugin e funzionalità aggiuntive per

implementare i requisiti anche più basilari. Questo implica la necessità di gestire i conseguenti problemi di

11

integrazione. Inoltre, il framework si limita a fornire strumenti implementativi, ignorando tutte le altre fasi

dello sviluppo di una applicazione mobile, a partire dalla prototipizzazione stessa.

2.1.3 Ambienti di sviluppo

Deco IDE

Deco IDE [20] è uno strumento per lo sviluppo intelligente di applicazione mobili basate su ReactNative.

Esso si configura come un ibrido tra un IDE e uno strumento grafico di prototipizzazione. Come i primi, esso consente di lavorare sul codice sorgente dell’applicazione e, come i secondi, consente di avere una preview dell’applicazione corrente, di inserire componenti tramite drag and drop direttamente da una palette, e di personalizzarli modificando graficamente parte delle proprietà. Le proprietà modificabili devono avere tipi di dato primitivi. La definizione di quali proprietà di un componente non predefinito possano essere aggiunge al pannello di modifica grafica viene effettuata manualmente dallo sviluppatore attraverso la creazione di appositi metadati. La definizione dei metadati stessi è agevolata dall’IDE, ma resta comunque un task da svolgere manualmente.

Sebbene l’idea sia ottima, il prodotto si presenta ad uno stato ancora abbastanza embrionale per essere utilizzato per progetti di larga scala. Più che un IDE vero e proprio, questo tool è classificabile come un editor guidato dalla sintassi.

La limitazione principale di questo strumento risiede nella sua ridotta disponibilità: esso infatti è disponibile solo per MacOS. Inoltre, questo IDE non contiene strumenti per definire graficamente le interfacce utente come le sue controparti native AndroidStudio e XCode. DecoIde manca, inoltre, delle funzionalità tipiche di un IDE, come ad esempio la possibilità di esportare il bundle dell’applicazione pronto per lo store, o un’integrazione con i popolari sistemi di controllo di versione, o la connessione con strumenti di debugging.

Figura 2.4 Interfaccia grafica di DecoIDE

JetBrains Android Studio

Android Studio [21] è lo strumento ufficiale per lo sviluppo di applicazioni per il sistema operativo Android.

Tale strumento è sviluppato da Google a partire dal popolare IDE Java IntelliJ Idea.

Android Studio offre un editor intelligente per i linguaggi di programmazione Java, C/C++ e Kotlin (l’ultimo

linguaggio rilasciato per lo sviluppo Android, equivalente al Java ma sintatticamente più conciso). Le

interfacce possono essere progettate graficamente tramite il componente Layout Editor, che consente la

definizione di molti modelli di layout, la personalizzazione dei componenti inseriti, la definizione di vincoli di

12

posizionamento e di dimensione, e la generazione automatica del file XML necessario ad Android per

rendere il layout progettato. Oltre agli strumenti caratteristici di tutti gli IDE, come l’integrazione con

sistemi di controllo di versione o l’automazione della pipeline di compilazione (ottenuta nel caso specifico

utilizzando gradle), Android Studio include un emulatore per il sistema operativo Android. Tale emulatore

permette all’utente di definire diversi dispositivi virtuali, ciascuno personalizzabile relativamente a versione

del sistema operativo utilizzata, modello di dispositivo, memoria di lavoro e memoria di massa

equipaggiata, sensori disponibili e molte altre opzioni secondarie. Il dispositivo così definito può essere

avviato ed essere utilizzato come un dispositivo reale. È, quindi, possibile installare la propria applicazione a

fini di debugging, attività per cui l’IDE offre un supporto dedicato. In alternativa è possibile utilizzare anche

il proprio device, purché la modalità sviluppatore sia abilitata.

Oltre al fatto che questo strumento supporta solamente il sistema operativo Android, e non è quindi

utilizzabile per uno sviluppo multipiattaforma, esso manca anche di strumenti utili ad unire le attività di

prototipizzazione e specifica con le attività di implementazione, limitando così la propria platea di

utilizzatori ai soli sviluppatori. Inoltre, esso non consente di progettare la navigazione all’interno

dell’applicazione in the wide. La navigazione è, infatti, implementata tramite l’invio di Intent al sistema

operativo, in modo locale alle singole scene che sono sorgenti dell’azione di navigazione. In questo modo

l’implementazione della navigazione risulta spezzata e distribuita nelle varie scene, e non è possibile averne

una visione d’insieme.

Apple XCode

XCode [22] è l’unico IDE supportato da Apple per la creazione di qualsiasi software per qualsiasi dispositivo

che esegua sistemi operativi della casa di Cupertino, dai driver per MacOS alle applicazioni mobile per

Apple Watch. In questa sezione verranno analizzate solamente le funzionalità a supporto dello sviluppo di

applicazioni mobili.

Dal punto di vista dei linguaggi di programmazione, il supporto di XCode è limitato al solo linguaggio Swift

4. Tale linguaggio è conciso, moderno e semplice da utilizzare, anche se non molto diffuso a causa della sua

specificità di utilizzo, limitata ai soli dispositivi Apple.

XCode automatizza molto il processo di sviluppo permettendo di svolgere moltissime attività attraverso

maschere e finestre di dialogo, senza praticamente mai scrivere un file di proprietà o di dipendenze, o il

manifesto dell’applicazione, o altri file di struttura. Questo rappresenta un punto di forza dal punto di vista

di un neofita, ma anche un fattore limitante per uno sviluppatore più esperto che si trova costretto nel

limite delle funzionalità esposte per via grafica. Lo strumento principe di questo approccio grafico è

l’Interface Builder, un potente motore di definizione di interfacce basato su drag and drop. Interface

Builder si affianca ad AutoLayout, un layout system che permette di gestire automaticamente i vincoli e il

comportamento dei singoli componenti. XCode, inoltre, integra uno strumento per la definizione grafica

delle StoryBoard, ovvero delle strutture di collegamento tra le varie schermate, dei pattern di navigazione

utilizzati e delle animazioni associate alle transizioni. In questo modo la progettazione della user

experience, dalla navigazione in the wide alla disposizione sullo schermo dei singoli componenti, è gestita

interamente tramite interfacce grafiche e il codice relativo viene automaticamente generato. Da ultimo,

XCode integra un simulatore per tutti i device Apple, in modo da poter testare la propria applicazione senza

acquistare la licenza da sviluppatore.

Nonostante sia un prodotto ottimo e curato fin nei minimi dettagli, XCode presenta alcune limitazioni. In

primo luogo, XCode può essere installato solo su dispositivi che eseguono OSX. Questa scelta della casa

13

produttrice restringe la platea di sviluppatori ai possessori di un dispositivo Mac, il cui costo di acquisizione

non è indifferente. In secondo luogo, il fatto che sia l’unico strumento di sviluppo supportato da Apple fa sì

che esso sia costretto ad integrare un numero enorme di funzionalità diverse. Questo si concretizza in uno

strumento abbastanza pesante e con un numero estremamente elevato di funzionalità che non interessano

al tipico sviluppatore mobile. Infine, questo strumento è rivolto solamente all’implementazione di una

applicazione, e non ha alcun tipo di supporto per le altre fasi di sviluppo, in particolare non supporta la

prototipizzazione e non presenta possibilità di integrazione o di comunicazione con strumenti di

prototipizzazione.

14

2.2 Presentazione della soluzione La descrizione dello stato dell’arte evidenzia alcune necessità fondamentali per il successo nel mercato

mobile

Sviluppo cross-platform

Condivisione di informazioni dal prototipo all’implementazione

Performance del processo di sviluppo

Performance del prodotto finito

In questo lavoro di tesi si vuole proporre una soluzione a queste sfide attraverso la realizzazione di

strumenti a supporto delle attività di prototipizzazione e di conseguente generazione automatica dell’app a

partire dal prototipo, adottando una soluzione cross-platform.

La soluzione proposta si basa sulla produzione di un’applicazione mobile a partire dalla definizione di un

modello strutturato dell’applicazione stessa. Questo approccio prende il nome di model – driven, ed è stato

oggetto di diversi studi da parte della comunità scientifica [23] [24] [25]. Esso prevede la definizione di una

struttura formale in cui memorizzare tutti gli elementi utili alla produzione del prodotto finito. Tale

struttura prende il nome di modello, e viene definito in base al dominio di applicazione dell’approccio

stesso. In questo lavoro viene adottato un approccio di tipo model – driven come punto di incontro tra la

progettazione grafica e la prototipizzazione di un’applicazione e il codice vero e proprio. Tale modello può,

infatti, essere definito a partire dall’attività di prototipizzazione dell’applicazione, e può in seguito essere

utilizzato in attività di generazione automatica del codice eseguibile, data la sua struttura formale.

Secondo questo schema, questo lavoro prevede la produzione di due strumenti: Protocode e

MobileCodeGenerator. Il primo supporta lo sviluppatore durante la prototipizzazione e la modellazione

dell’applicazione e rende disponibili le funzionalità caratteristiche di uno strumento di prototipizzazione:

inserimento di componenti tramite drag and drop, personalizzazione dei componenti stessi tramite

interfaccia grafica e possibilità di avere un’anteprima visuale immediata ed interattiva del lavoro. L’output

di questo strumento, e l’input del secondo strumento, è il modello dell’applicazione. Il secondo strumento

è un generatore di codice capace di tradurre il modello in input in un progetto di applicazione mobile

basato sul pattern architetturale Model – View – Controller.

Entrambi gli strumenti presentati non sono interamente frutto di questo lavoro di tesi. La prima versione di

Mobile Code Generator è nata nel 2012 dal lavoro di Gregorio Perego e Stefania Pezzetti [3] e

successivamente migliorata da Mattia Natali nel 2013 [4] che ha inoltre introdotto lo strumento Protocode

per la prototipizzazione della componente View. Nell’A.A. 2015/2016 Aldo Pintus [5] ha aggiunto la

possibilità di modellare la componente View di applicazioni per smartwatch. Infine, nell’A.A. 2016/2017

Alessio Rossotti [6] ha introdotto la possibilità di modellare la componente Model dell’applicazione.

Questo lavoro di tesi interviene su entrambi gli strumenti. Protocode viene aggiornato al fine di permettere

la modellazione e prototipizzazione grafica anche di alcune componenti afferenti all’area di competenza

della componente Controller, in particolare per quanto riguarda il binding di dati nella View, completando

così l’implementazione del pattern architetturale MVC. Le aree relative alla prototipizzazione di Model e

View restano inalterate. MobileCodeGenerator viene arricchito di un nuovo target della procedura di

generazione: la produzione di codice cross – platform per il framework ReactNative. Le motivazioni alla

base di tale scelta verranno dettagliate nel capitolo §5

15

Tale soluzione presenta elementi distintivi da tutti gli strumenti presenti sul mercato: Proto.io [7],

MarvelApp [10] e Adobe Experience Design CC [11] sono eccellenti nella prototipizzazione, ma mancano

della possibilità di generare automaticamente il codice sorgente dell’applicazione; Xamarin [12], PhoneGap

[15] e Titanium [18] sono buone soluzioni al problema dello sviluppo cross – platform, ma non si

interfacciano al meglio con strumenti di prototipizzazione; AndroidStudio [21] e XCode [22] non permettono

lo sviluppo cross – platform, pur permettendo la definizione grafica di molte componenti dell’applicazione;

DecoIDE [20] non è un tool basato su modelli ma su un riconoscimento efficace della sintassi e sulla

conseguente possibilità di agire, attraverso strumenti grafici, sulle proprietà passate ai componenti.

Rispetto ai requisiti richiesti, la soluzione proposta permette di ottenere un’applicazione cross-platform la

cui efficienza è supportata dal framework scelto. L’interazione tra prototipizzazione e implementazione è

garantita dal modello. Quanto alla performance del processo di sviluppo, essa verrà esaminata nel capitolo

§6

2.3 Struttura del documento Nei prossimi capitoli di questo documento verrà descritto l’approccio delineato nel paragrafo §2.2 e ne

verrà valutata la performance. In particolare

Il capitolo 3 descrive la struttura formale del modello di applicazione usato come interfaccia tra

Protocode e MobileCodeGenerator

Il capitolo 4 presenta le funzionalità e l’architettura di Protocode

Il capitolo 5 presenta le funzionalità e l’architettura di MobileCodeGenerator

Il capitolo 6 effettua una valutazione delle performance della soluzione proposta

Il capitolo 7 contiene alcune considerazioni finali sul lavoro svolto

16

3 Modello dell’applicazione L’approccio model-driven alla generazione di applicazioni mobili richiede che venga definito un modello

formale e strutturato di applicazione mobile. La definizione di tale modello costituisce un elemento di

primaria importanza in questo lavoro di tesi, dato che esso costituisce l’interfaccia tra i due strumenti che

compongono la soluzione proposta e il punto di partenza per l’implementazione degli stessi. In questo

capitolo viene presentata e discussa la struttura di tale modello. Al fine di agevolare la lettura, il modello

viene spezzato su una collezione di class diagram. Entità che appaiono in diagrammi diversi con lo stesso

nome sono da considerarsi la stessa entità.

Il modello descritto nei paragrafi successivi deriva in gran parte dai lavori di tesi precedenti ( [3] [4] [5] [6] ),

solo la parte relativa alla modellazione della componente Controller è frutto di questo lavoro di tesi.

3.1 Modello della View

3.1.1 Struttura generale

La componente visiva di un’applicazione mobile è essenzialmente costituita da un insieme di controlli, i

quali hanno il compito di interagire con l’utente, fornendo o raccogliendo informazioni. È intuitivo pensare

come un testo statico o un’immagine interagiscano con l’utente fornendogli informazioni testuali e

grafiche, e come una casella di testo interagisca con l’utente attraverso l’inserimento dei dati.

È possibile raggruppare i controlli a formare strutture che definiamo viewController. Un viewController è

costituito da un insieme di controlli aggregati a costituire una funzionalità precisa e riutilizzabile all’interno

dell’applicazione. È possibile pensare ad un viewController come ad una vista all’interno dell’applicazione.

I viewController possono essere a loro volta aggregati a formare scene. Ogni scena corrisponde ad un

momento nella navigazione all’interno di una applicazione. Le modalità con cui i viewController sono

aggregati possono essere varie: una scena può mostrare un viewController a tutto schermo, oppure può

fornire un menù a schede per ottenere la navigazione tra diversi viewController, sempre mostrati a tutto

schermo, oppure può frazionare la superficie disponibile sullo schermo e destinarne ciascuna parte ad un

diverso viewController.

Nel caso di logiche di aggregazione complesse, come ad esempio il partizionamento dello schermo, una

scena utilizza un componente, chiamato parentViewController, per gestire il layout. Un

parentViewController si pone ad un livello superiore rispetto ai viewController ordinari chiamati a

comporre la scena, ma può comunque essere considerato un particolare viewController in cui sono inseriti

appositi controlli, chiamati container, la cui funzione è quella di incapsulare a loro volta un viewController.

Un’ulteriore problematica risolta tramite l’adozione dei parentViewController riguarda l’adattabilità del

layout a dispositivi diversi: una scena può possedere da zero a due parentViewController, ciascuno

associato ad una classe di dispositivi (smartphone o tablet). Durante l’esecuzione, la scena seleziona il

parentViewController corrispondente al tipo di device che sta eseguendo l’applicazione, variando la

modalità di visualizzazione dei viewController in essa inseriti in modo anche radicale.

La View di una applicazione mobile è, quindi, definita da quattro livelli gerarchici: la scena, i

parentViewController, i viewController, i controlli utente. Il livello relativo ai parentViewController è

opzionale e può essere omesso nei casi in cui la logica di presentazione dei viewController componenti una

scena sia semplice.

17

Figura 3.1 Modello degli elementi di alto livello della componente View

La figura 3.1 descrive il modo in cui il modello di applicazione mobile in uso in questo lavoro di tesi

formalizza i livelli alti di questa gerarchia.

Application

La classe Application descrive le proprietà generali di un’applicazione mobile, il dettaglio delle quali è

riportato nella seguente tabella

name Il nome dell’applicazione

companyIdentifier L’identificativo del produttore dell’applicazione, creato secondo le regole a cui conformano i prefissi dei nomi dei package di una classica applicazione Java

scenes La collezione delle scene definite per l’applicazione

viewControllers La collezione dei viewController definiti per l’applicazione Tabella 3.1 Campi di Application

È necessario definire già a livello di Application la collezione dei viewController definiti per l’applicazione in

quanto la definizione di un viewController è indipendente dalla definizione della/e scena/e in cui esso sarà

incluso, e pertanto non è possibile stabilire una relazione di composizione tra una scena e i relativi

viewController componenti. Tale relazione di composizione andrebbe infatti ad impedire il riuso dei

viewController tra diverse scene, e questo farebbe venir meno il motivo stesso della definizione dei

viewController.

Scene

La classe Scene è dedicata a modellare le scene, in base alla descrizione di cui sopra. Le proprietà di una

scena sono dettagliate nella seguente tabella

18

id Identificativo univoco della scena (otto caratteri alfanumerici)

name Il nome distintivo della scena, assegnato dal modellatore

launcher Indica se una scena costituisce il punto di inizio della navigazione all’interno dell’applicazione

typeSmartphone Indica la logica di presentazione dei viewController componenti la scena su un device di tipo smartphone

typeTablet Indica la logica di presentazione dei viewController componenti la scena su un device di tipo tablet

childViewControllers Lista di riferimenti ai viewController componenti la scena

parentViewControllers Lista dei parentViewController definiti per la scena Tabella 3.2 Campi di scene

I campi typeSmartphone e typeTablet indicano la modalità di presentazione sullo schermo dei

viewController componenti la scena rispettivamente su smartphone e tablet. I valori possibili per tali campi

sono

SINGLE_VC: la scena presenta un solo viewController a tutto schermo. Nel caso in cui

childViewControllers contenga più elementi, viene selezionato il primo di essi. Il passaggio ad

eventuali altri elementi è a carico del designer, che lo può implementare tramite apposite azioni di

navigazione (§3.1.6)

SINGLE_VC_TAB: la scena presenta un solo viewController a tutto schermo. Nel caso in cui

childViewControllers contenga più elementi, viene mostrato il primo di essi. Il passaggio ad

eventuali altri elementi è possibile attraverso un menù a schede fornito automaticamente, oppure

tramite apposite azioni di navigazione (§3.1.6). La definizione di azioni di navigazione tuttavia non

altera il menù a schede, che contiene sempre e comunque una scheda per ogni viewController

componente

MULTI_VC: la scena mostra più viewController contemporaneamente, in base ad una logica

definita dal progettista. Tale logica è descritta attraverso un elemento di tipo parentViewController

contenuto nella lista parentViewControllers. La selezione del parentViewController dalla lista

avviene secondo le seguenti modalità:

o Se typeSmartphone ha valore MULTI_VC e typeTablet non ha valore MULTI_VC la lista

parentViewControllers contiene un solo elemento e tale elemento è il parentViewController

da utilizzare quando la scena viene mostrata su smartphone

o Se typeSmartphone non ha valore MULTI_VC e typeTablet ha valore MULTI_VC la lista

parentViewControllers contiene un solo elemento e tale elemento è il parentViewController

da utilizzare quando la scena viene mostrata su tablet

o Se typeSmartphone non ha valore MULTI_VC e typeTablet non ha valore MULTI_VC la lista

parentViewControllers è vuota

o Se typeSmartphone ha valore MULTI_VC e typeTablet ha valore MULTI_VC la lista

parentViewControllers contiene due elementi di tipo parentViewController di cui il primo

implementa la logica di visualizzazione su smartphone e il secondo su tablet

19

ViewController

La classe ViewController è dedicata alla modellazione dei viewController definiti per l’applicazione. Tale

classe è il modello di riferimento anche per i parentViewController, in quanto particolari viewController

dedicati a raggruppare controlli che agiscono come box per l’inclusione di altri viewController. La tabella 3.3

dettaglia le proprietà di un viewController.

id Identificativo univoco del viewController (otto caratteri alfanumerici) name Nome distintivo del viewController assegnato dal modellatore. Nel

momento in cui il viewController viene inserito in una scena con modalità di visualizzazione SCENE_VC_TAB, il titolo della scheda del menù associata al viewController corrisponde al suo nome.

backgroundColor Il colore di sfondo del viewController, espresso come stringa RGB esadecimale preceduta dal carattere #. E’ opzionale, e di default vale #FFFFFF (colore bianco)

backgroundImage Il percorso dell’immagine da utilizzare come sfondo del viewController Tabella 3.3 Campi di ViewController

La modellazione di un viewController non è limitata a tali campi, e verrà completata procedendo nella

trattazione (§3.1.3, §3.1.4, §3.1.5, §3.1.6, §3.1.7, §3.3).

ChildViewController

La classe ChildViewController permette la definizione del legame tra una scena e i viewController che la

compongono. Un oggetto di questa classe contiene solamente due riferimenti: il primo alla scena che

include il viewController, il secondo al viewController incluso. Al fine di preservare la riusabilità dei

viewController, quest’ultima relazione è unidirezionale.

3.1.2 Modellazione astratta dei controlli utente

Come descritto nel paragrafo §3.1.1, gli elementi di base utilizzati per la definizione della componente View

di un’applicazione sono i controlli utente. La modellazione di tali controlli richiede, quindi, particolare

attenzione. In questo capitolo verrà dapprima presentato il modello generale di un controllo utente e in

seguito i vari controlli che è possibile utilizzare all’interno di questo modello.

Figura 3.2 Modello astratto dei controlli utente

20

La figura 3.2 descrive l’astrazione utilizzata in questo modello per descrivere un generico controllo utente.

Ciò che caratterizza tutti i controlli utente è il fatto che essi devono essere posizionati sullo schermo

secondo una logica anche molto complessa, data la variabilità, in termini di dimensioni, degli schermi a cui

l’applicazione deve potersi adattare. Tutte le classi coinvolte nel modello astratto di un controllo utente

sono destinate a descrivere la logica di apparizione del controllo stesso sullo schermo.

Le possibili dinamiche di posizionamento definite in questo modello sono tre: il posizionamento assoluto, il

posizionamento vincolato e le catene di controlli. Il posizionamento assoluto prevede la definizione statica

delle coordinate (espresse in dip) dell’angolo superiore sinistro (destro per ambienti RTL) del componente.

Il posizionamento vincolato prevede la definizione di vincoli di posizione tra diversi elementi, o tra un

elemento e il suo contenitore. Tali vincoli si basano su condizioni di allineamento di elementi notevoli,

quali, ad esempio, il centro del controllo o il lato superiore. Il meccanismo di posizionamento vincolato

viene descritto nel capitolo PositionConstraint. Il posizionamento mediante catena di controlli prevede

l’appartenenza di un controllo ad una catena, caratterizzata da un asse (x o y) e da una politica di

suddivisione dello spazio. I controlli che fanno parte di una catena vengono allineati lungo l’asse della

catena stessa, e lo spazio disponibile sull’asse viene distribuito tra i vari controlli in base alla politica scelta.

Per maggiori dettagli si veda il capitolo ControlChain. Come per il posizionamento, è possibile esprimere

anche vincoli riguardo alla dimensione di un elemento. È possibile vincolare la larghezza, l’altezza o il loro

rapporto a valori statici (espressi in dip) o percentuali rispetto al contenitore. I vincoli di dimensione sono

dettagliati nel paragrafo DimensionConstraint.

UiControl

La classe astratta UiControl contiene le proprietà basilari di qualsiasi controllo utente, le quali sono

dettagliate in tabella 3.4. Essa è separata da UiPhoneControl in quanto le proprietà ivi definite sono comuni

anche a controlli destinati a piattaforme smartwatch o di altro genere.

id Identificativo univoco del controllo (stringa alfanumerica di 8 caratteri)

posX Coordinata orizzontale per il posizionamento assoluto

posY Coordinata verticale per il posizionamento assoluto

marginTop Dimensione, in dip, dell’ampiezza dello spazio vuoto sopra il lato superiore del controllo

marginBottom Dimensione, in dip, dell’ampiezza dello spazio vuoto sotto il lato inferiore del controllo

marginStart Dimensione, in dip, dell’ampiezza dello spazio vuoto oltre il lato sinistro (destro in ambienti RTL) del controllo

marginEnd Dimensione, in dip, dell’ampiezza dello spazio vuoto oltre il lato destro (sinistro in ambienti RTL) del controllo

paddingTop Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato superiore del controllo e il contenuto del controllo stesso

paddingBottom Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato inferiore del controllo e il contenuto del controllo stesso

paddingStart Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato sinistro (destro in ambienti RTL) del controllo e il contenuto del controllo stesso

paddingEnd Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato destro (sinistro in ambienti RTL) del controllo e il contenuto del controllo stesso

Tabella 3.4 Campi di UiControl

Questa classe di controlli fornisce, attraverso gli attributi posX e posY, il meccanismo di posizionamento

assoluto.

21

UiPhoneControl

La classe astratta UiPhoneControl modella un controllo utente di un’applicazione per smartphone o tablet.

Essa si occupa di gestire meccanismi di posizionamento avanzati, e fornisce la possibilità di specificare

vincoli di posizione. La tabella 3.5 descrive le proprietà di un UiPhoneControl.

defaultWidth Indica la larghezza che assume il componente in assenza di vincoli di dimensione relativi

defaultHeight Indica l’altezza che assume il componente in assenza di vincoli di dimensione relativi

controlChain Indica la catena di controlli di cui il controllo è parte. Se il controllo non è parte di alcuna catena questo attributo ha valore nullo.

indexInChain Indica la posizione di un controllo all’interno della catena in cui è inserito. Le posizioni sono contate a partire da 1. Se il controllo non è parte di alcuna catena questo attributo ha valore nullo.

precedentInChain Se il controllo corrente è inserito in una catena questo attributo contiene un riferimento al controllo che lo precede nella catena stessa. Se il controllo non è parte di alcuna catena o è il primo elemento di una catena questo attributo ha valore nullo.

followingInChain Se il controllo corrente è inserito in una catena questo attributo contiene un riferimento al controllo che lo segue nella catena stessa. Se il controllo non è parte di alcuna catena o è l’ultimo elemento di una catena questo attributo ha valore nullo.

weight Se il controllo corrente è inserito in una catena di tipo weighted, questo attributo contiene il peso relativo del controllo nella catena. Se il controllo non è parte di alcuna catena questo attributo ha valore nullo.

positionConstraints Collezione di vincoli di posizione applicati a questo elemento

dimensionConstraint Vincolo di dimensione applicato a questo elemento. È opzionale, e qualora non sia presente le dimensioni dell’elemento assumono i valori specificati da defaultWidth e defaultHeight

Tabella 3.5 Campi di UiPhoneControl

È immediato notare che la totalità degli attributi propri di UiPhoneControl è destinata alla gestione del

posizionamento e delle dimensioni di un controllo.

DimensionConstraint

La classe DimensionConstraint indica un vincolo applicato ad una o entrambe le dimensioni di un elemento.

La tabella 3.6 presenta i campi di tale classe, ciascuno dei quali corrisponde ad un preciso vincolo. Alcuni

vincoli si riferiscono all’elemento contenitore che, in questo modello, coincide sempre con il viewController

in cui il controllo è inserito.

fixedWidth Impone all’elemento a cui è applicato una larghezza assoluta

fixedHeight Impone all’elemento a cui è applicato un’altezza assoluta

fixedRatio Impone all’elemento a cui è applicato un rapporto fisso tra larghezza e altezza, codificato come un numero razionale nella forma numeratore:denominatore.

widthPercent Impone all’elemento a cui è applicato una larghezza percentuale rispetto al contenitore

heightPercent Impone all’elemento a cui è applicato un’altezza percentuale rispetto al contenitore

Tabella 3.6 Campi di DimensionConstraint

22

Al fine di non creare situazioni di vincoli non risolvibili o contraddittori, è possibile specificare solo due

attributi per ogni istanza di DimensionConstraint. Le combinazioni possibili sono riportate nella seguente

tabella 3.7.

fixedWidth fixedHeight fixedRatio widthPercent heightPercent

fixedWidth -

fixedHeight - fixedRatio -

widthPercent -

heightPercent - Tabella 3.7 Possibili combinazioni di vincoli di dimensione

PositionConstraint

Ogni controllo inserito in un viewController possiede una collezione di vincoli ad esso applicati, ciascuno dei

quali è un’istanza della classe PositionConstraint.

Ogni vincolo è definito tra due elementi: il primo è l’elemento a cui il vincolo è applicato e a cui il vincolo

appartiene, il secondo è l’elemento che agisce come riferimento per quel vincolo. Il primo elemento verrà

chiamato controllo proprietario, mentre il secondo controllo di appoggio. Il ruolo del controllo di appoggio

può essere assunto anche dal viewController in cui il controllo che possiede il vincolo è inserito: un vincolo

di questo tipo è definito vincolo con il padre.

Definiamo bounding box di un controllo utente il più piccolo rettangolo capace di contenere l’intero

contenuto del controllo ed il padding ad esso applicato. Definiamo quindi i seguenti elementi rilevanti di

controllo utente

top: la coordinata lungo l’asse verticale del lato superiore del bounding box del controllo

bottom: la coordinata lungo l’asse verticale del lato inferiore del bounding box del controllo

centerY: la posizione del centro del bounding box lungo l’asse verticale del controllo

left: la coordinata lungo l’asse orizzontale del lato sinistro del bounding box del controllo

right: la coordinata lungo l’asse orizzontale del lato destro del bounding box del controllo

centerX: la posizione del centro del bounding box lungo l’asse verticale del controllo

Esprimere un vincolo significa stabilire una relazione tra un elemento rilevante del controllo proprietario e

un elemento rilevante del controllo di appoggio. Tale relazione è significativa solo per alcune coppie di

elementi rilevanti, le quali sono descritte nella tabella 3.8. I valori contrassegnati da un asterisco indicano

che tale combinazione è possibile soltanto se il vincolo non viene stabilito con il padre.

Elemento di appoggio

Elemento proprietario

top bottom centerY left right centerX top *

bottom *

centerY

left *

right *

centerX

Tabella 3.8 Relazioni possibili tra elementi rilevanti

Ad esempio è possibile relazionare il top dell’elemento proprietario con il bottom dell’elemento di

riferimento per creare un layout verticale in cui i due componenti sono disposti uno sotto l’altro.

23

Tali combinazioni tuttavia non escludono la possibilità di definire, per un elemento, un insieme di vincoli

non risolvibile, ovvero un insieme in cui siano presenti due o più vincoli in contraddizione. La seguente

tabella 3.9 indica quali elementi rilevanti siano contemporaneamente vincolabili. In caso di più vincoli

applicati, gli elementi ancora vincolabili sono dati dall’intersezione logica delle righe relative ai vincoli già

presenti.

Elementi rilevanti ancora liberi per la definizione di ulteriori vincoli

Elemento rilevante vincolato

top bottom centerY left right centerX top

bottom

centerY

left

right

centerX

Tabella 3.9 Coesistenza di vincoli

Nel caso in cui un elemento appartenga ad una catena di controlli (si veda ControlChain):

sono considerati già vincolati gli elementi rilevanti top, bottom e centerY nel caso in cui la catena

abbia asse verticale

sono considerati già vincolati gli elementi rilevanti left, right e centerX nel caso in cui la catena

abbia asse orizzontale

La definizione di vincolo che abbiamo dato consente di definire la relazione R(x, y) che contiene tutte le

coppie di controlli (x,y) tali per cui esiste un vincolo appartenente ad x e avente come appoggio y. Tale

relazione è rappresentabile mediante un grafo. In Protocode, tale grafo è un DAG (Directed Acyclic Graph,

grafo orientato aciclico). Nel caso in cui l’utente inserisca un vincolo che introduce un ciclo nel suddetto

grafo, tale vincolo viene marcato come invalido ed escluso dal set di vincoli effettivamente applicati

all’elemento stesso.

id Identificativo univoco del vincolo (stringa alfanumerica di 8 caratteri)

layoutEdge Elemento rilevante del controllo proprietario coinvolto nel vincolo

withParent Vero se il controllo di appoggio è l’elemento contenitore, falso altrimenti

referenceLayoutEdge Elemento rilevante del controllo di appoggio coinvolto nel vincolo

referenceElement Riferimento al controllo di appoggio del vincolo. Se il vincolo è con il padre questo attributo ha valore nullo

Tabella 3.10 Campi di PositionConstraint

ControlChain

Una control chain, o catena di controlli, è un meccanismo di layout introdotto al fine di rendere possibile la

ripartizione dello spazio disponibile tra diversi controlli. Le dinamiche del posizionamento vincolato

presentano due limitazioni alla definizione di layout complessi: sono separate dalle dinamiche di calcolo

delle dimensioni dei componenti e operano su due componenti alla volta, perdendo quindi la visione

d’insieme.

Una catena di controlli permette la distribuzione di una lista ordinata di controlli lungo un asse fissato, che

può essere l’asse verticale o l’asse orizzontale. L’ordine dei componenti nella lista associata ad una catena

riflette l’ordine in cui essi vengono disposti lungo l’asse: se l’asse è orizzontale, i controlli sono distribuiti da

sinistra a destra; se l’asse è verticale, i controlli sono distribuiti dall’alto verso il basso. Nel contesto di

24

questo modello sono definite quattro tipologie di catene, ciascuna corrispondente ad una diversa dinamica

di distribuzione: spread, spread inside, packed e weighted.

Definiamo punto di inizio dell’asse di una catena orizzontale la posizione lungo l’asse del bordo sinistro

dell’elemento contenitore, e punto finale la posizione del bordo destro dello stesso lungo l’asse della

catena. Analogamente, per una catena verticale il punto iniziale è definito dal bordo superiore e il punto

finale dal bordo inferiore. Definiamo altresì alcune funzioni e variabili che verranno utilizzate durante la

definizione delle varie dinamiche di distribuzione

𝑛 è il numero di controlli inseriti nella catena

𝑚𝑒𝑚𝑏𝑒𝑟𝑠 è la lista che contiene i controlli inseriti nella catena

𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖] è l’𝑖-esimo controllo facente parte della catena (per 0 ≤ 𝑖 ≤ 𝑛)

𝑑𝑖𝑚(𝑐) è una funzione che restituisce la dimensione di un controllo lungo l’asse della catena,

margini inclusi. Se la catena è verticale, tale funzione restituisce la somma tra l’altezza, il margine

superiore e il margine inferiore del controllo 𝑐. Se invece la catena è orizzontale tale funzione

restituisce la somma tra la larghezza, il margine sinistro e il margine destro del controllo 𝑐

𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑐) è una funzione che restituisce il margine che precede il controllo 𝑐 lungo l’asse

della catena. Tale margine coincide con il margine sinistro se la catena è orizzontale e con il

margine superiore se la catena è verticale.

𝐿𝐸𝑁 la lunghezza dell’asse (in dip)

Definiamo infine posizione di un elemento la coordinata lungo l’asse del primo punto appartenente

all’elemento (il margine non è considerato parte dell’elemento).

In una catena di tipo spread, i componenti sono distribuiti lungo l’asse selezionato in modo che sia costante

la dimensione dello spazio bianco tra ciascuna coppia di componenti consecutivi; tale spazio si presenta

anche tra il primo controllo e il punto di inizio dell’asse e tra l’ultimo controllo e il punto finale dell’asse.

Qualora gli elementi della catena possedessero un margine non nullo lungo l’asse della catena, tale margine

viene calcolato come parte della dimensione dell’elemento stesso. La dimensione dei componenti di una

catena di tipo spread non è influenzata dalla loro appartenenza alla stessa. In particolare, lo spazio libero

totale 𝐿 è calcolato come 𝐿 = 𝐿𝐸𝑁 − ∑ 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])𝑛𝑖=0 . A partire da 𝐿 viene calcolato lo spazio tra

due componenti 𝑠 =𝐿

𝑛+1. Per quanto detto, l’𝑖-esimo elemento della catena avrà posizione 𝑝(𝑖) = 𝑠 ∗

(𝑖 + 1) + ∑ 𝑑𝑖𝑚 (𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑗])𝑖−1𝑗=0 + 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖]) lungo l’asse della catena.

Una catena di tipo spread inside segue le stesse regole di una catena di tipo spread, con la sola differenza

che non vi è alcuno spazio bianco (a meno del margine dell’elemento) né tra il primo elemento della catena

e il punto iniziale dell’asse né tra l’ultimo elemento della catena e il punto finale dell’asse. Rispetto al caso

precedente quindi le equazioni di calcolo della posizione risultano così modificate

𝐿 = 𝐿𝐸𝑁 − ∑ 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])

𝑛

𝑖=0

𝑠 =𝐿

𝑛 − 1

𝑝(𝑖) = 𝑠 ∗ 𝑖 + ∑ 𝑑𝑖𝑚 (𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑗])

𝑖−1

𝑗=0

+ 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])

25

Una catena di tipo packed distribuisce i controlli in modo che ognuno di essi sia affiancato al controllo

precedente e successivo senza spazio bianco a meno del margine e di una costante definita per la catena e

detta spacing: lo spazio bianco tra elementi adiacenti è dato dalla sola somma del margine finale del primo

componente con il margine iniziale del primo componente e con tale costante. La dimensione dei controlli

appartenenti ad una catena di tipo packed non è influenzata dalla loro appartenenza alla stessa. Lo spazio

in eccesso è suddiviso in due e disposto prima e dopo il gruppo di controlli. La suddivisione è controllata da

un parametro della catena chiamato 𝑏𝑖𝑎𝑠. La posizione dei vari controlli inseriti nella catena può essere

definita a partire da una funzione ausiliaria 𝑧(𝑖) nel seguente modo

𝐿 = 𝐿𝐸𝑁 − (∑ 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])

𝑛

𝑖=0

+ (𝑛 − 1) ∗ 𝑠𝑝𝑎𝑐𝑖𝑛𝑔))

𝑧(0) = 𝐿 ∗ 𝑏𝑖𝑎𝑠

𝑧(𝑖) = 𝑧(𝑖 − 1) + 𝑠𝑝𝑎𝑐𝑖𝑛𝑔 + 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖 − 1]) per 1 ≤ 𝑖 ≤ 𝑛

𝑝(𝑖) = 𝑧(𝑖) + 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑖)

Una catena di tipo weighted agisce sulla dimensione lungo l’asse della catena dei controlli ad essa

appartenenti. Ad ogni elemento appartenente alla catena viene associato un numero intero detto weight o

peso. Sulla catena viene inoltre definito il parametro spacing, che definisce la dimensione dello spazio

bianco tra due componenti adiacenti. Tale spazio non viene inserito prima del primo componente o dopo

l’ultimo. La posizione 𝑝 e la dimensione 𝑔 di ciascun elemento appartenente alla catena possono essere

definite come segue (𝑝 e 𝑔 prendono sono parametriche in base all’indice del componente nella catena)

𝐿 = 𝐿𝐸𝑁 − (𝑛 − 1) ∗ 𝑠𝑝𝑎𝑐𝑖𝑛𝑔 𝑊 = ∑ 𝑤𝑒𝑖𝑔ℎ𝑡(𝑖)𝑛𝑖=0

𝑘(𝑖) =𝐿

𝑊∗ 𝑤𝑒𝑖𝑔ℎ𝑡(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])

𝑝(𝑖) = 𝑖 ∗ 𝑠𝑝𝑎𝑐𝑖𝑛𝑔 + ∑ 𝑘(𝑗) + 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑖)

𝑖−1

𝑗=0

𝑔(𝑖) = 𝑘(𝑖) − 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑖) − 𝑚𝑎𝑟𝑔𝑖𝑛𝐸𝑛𝑑(𝑖)

Tutte le rappresentazioni descritte possono essere modellate tramite la definizione della classe

controlChain in figura 3.2, i cui campi sono descritti nella seguente tabella 3.11.

id Identificativo univoco della catena (otto caratteri alfanumerici)

axis L’asse lungo cui la catena distribuisce i controlli ad essa appartenenti

type Il tipo di catena, che determina l’algoritmo di distribuzione

nControls Il numero di elementi inseriti nella catena

spacing Il parametro spacing descritto per le tipologie packed e weighted

bias Il parametro bias descritto per le catene di tipo packed Tabella 3.11 Campi di ControlChain

3.1.3 Modellazione dei controlli con interazione unidirezionale con l’utente

In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo

§3.1.2 per i controlli che presentano un’interazione unidirezionale con l’utente. I controlli in questo gruppo

infatti sono esclusivamente dedicati a fornire informazioni, di diversa tipologia, all’utente; non è possibile

26

per l’utente inserire informazioni nell’applicazione tramite questi controlli. Ciascuno dei controlli descritti

estende la classe UiPhoneControl descritta nel capitolo UiPhoneControl con attributi propri e caratteristici.

Figura 3.3 Modello dei componenti con interazione unidirezionale con l’utente

Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController presentata nel capitolo

ViewController viene arricchita delle seguenti relazioni

containers Collezione dei controlli di classe Container inseriti nel viewController. Questa collezione è non vuota solo per i parentViewController

cards Collezione dei controlli di tipo Card inseriti nel viewController

maps Collezione dei controlli di tipo Map inseriti nel viewController

textViews Collezione dei controlli di tipo TextView inseriti nel viewController

webViews Collezione dei controlli di tipo WebView inseriti nel viewController Tabella 3.12 Estensione di ViewController ai controlli con interazione unidirezionale

TextView

Una textView, detta anche casella di testo o label, è un controllo che contiene esclusivamente del testo

statico, nel senso che non può essere modificato direttamente dall’utente dell’applicazione. Al testo

contenuto in una textView possono essere applicate diverse personalizzazioni:

È possibile modificare la modalità di allineamento del testo all’interno della casella di testo

scegliendo tra l’allineamento a sinistra (predefinito), l’allineamento centrato e l’allineamento a

destra

È possibile applicare effetti di evidenziazione del testo scegliendo tra grassetto o corsivo. Nessuno

stile è applicato per impostazione predefinita.

È possibile modificare il colore del testo scegliendo qualsiasi colore dello spazio RGB

È possibile modificare la dimensione del testo (espressa in dip)

27

La classe che descrive una textView all’interno del modello possiede quindi i seguenti campi

content La stringa di testo contenuta nel controllo

textAlign La modalità di allineamento del testo

textDecoration La decorazione applicata al testo

textColor Il colore del testo

textSize La dimensione del testo Tabella 3.13 Campi di TextView

WebView

Una webView è un controllo capace di emulare il funzionamento di un browser e permettendo quindi di

includere una pagina web all’interno dell’applicazione. La pagina mostrata può essere una risorsa

disponibile in Internet o un file HTML statico incluso nell’applicazione. Il percorso locale o di rete del file

mostrato nella WebView è contenuto nel parametro HTMLFileName.

Map

Il controllo Map è in grado di mostrare all’utente una mappa stradale centrata su uno specifico punto della

superficie terrestre. Il punto su cui la mappa è inizialmente centrata viene scelto dal designer, ma l’utente

ha la possibilità di interagire con la mappa muovendola e cambiando quindi l’area geografica visualizzata.

Tale azione tuttavia non ha ripercussioni dirette sui dati inseriti nell’applicazione. Le proprietà di un

controllo Map sono descritte nella seguente tabella

lat Latitudine su cui la mappa è centrata dal designer

lon Longitudine su cui la mappa è centrata dal designer Tabella 3.14 Campi di Map

Card

Il controllo Card viene utilizzato per mostrare all’utente un contenuto e le azioni ad esso correlate. Tale

contenuto è caratterizzato da un titolo, un sottotitolo (che può anche essere una breve descrizione) e un

numero variabile di azioni. Il comportamento di tali azioni non è incluso in questo modello.

title Il titolo del contenuto della Card

subtitle Il sottotitolo, o una breve descrizione, del contenuto della Card

numActions Il numero di azioni associate al contenuto della Card. Non è possibile in questo modello personalizzare il nome o il comportamento delle diverse azioni

Tabella 3.15 Campi di Card

Container

Un controllo di classe Container può essere inserito solamente in un parentViewController. Un container si

occupa di delimitare una porzione della superficie del viewController in cui è inserito e di destinarla ad un

altro viewController, che viene istanziato su tale superficie. Questo controllo consente, quindi, di realizzare

qualsiasi logica arbitraria di suddivisione dello schermo tra diversi viewController, purché tale logica si

conformi all’ipotesi che l’area destinata ad un viewController ha forma rettangolare. La proprietà

childViewController contiene un riferimento al viewController che deve essere inserito nello spazio su cui il

container si estende.

3.1.4 Modellazione dei controlli multimediali

In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo

§3.1.2 per i controlli che presentano un’interazione multimediale con l’utente. In questo modello definiamo

28

controlli multimediali tutti quei controlli che utilizzano immagini, flussi audio o flussi video. Ciascuno dei

controlli descritti estende la classe UiPhoneControl con attributi propri e caratteristici.

Figura 3.4 Modellazione dei controlli multimediali

Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController viene arricchita delle

seguenti relazioni

audioPlayers Collezione dei controlli di classe AudioPlayer inseriti nel viewController

audioRecorder Riferimento all’eventuale controllo di classe AudioRecorder inserito nel viewController

imageViews Collezione dei controlli di classe ImageView inseriti nel viewController

photocameraController Riferimento all’eventuale controllo di classe PhotocameraController inserito nel viewController

videoViews Collezione dei controlli di classe VideoView inseriti nel viewController

videocameraController Riferimento all’eventuale controllo di tipo VideocameraController inserito nel viewController

Tabella 3.16 Estensione di ViewController ai controlli multimediali

Il modello in uso in questo lavoro di tesi permette la gestione di tre tipologie di elementi multimediali:

immagini, audio e video. Per ciascuna di esse, è definita una coppia di controlli: un controllo capace di

riprodurre un contenuto multimediale, l’altro controllo capace di acquisirlo. È possibile utilizzare i controlli

di riproduzione in modo autonomo o in modo combinato con un controllo di acquisizione. La modalità

autonoma prevede che il designer selezioni il contenuto da riprodurre e indichi il percorso, locale o di rete,

del file in cui tale contenuto è archiviato. La modalità combinata prevede che il designer colleghi un

controllo di acquisizione con un controllo di riproduzione: quest’ultimo mostrerà il risultato dell’ultima

operazione di acquisizione.

Ciascun controllo destinato alla riproduzione di elementi multimediali può, quindi, funzionare con tre

tipologie di sorgenti

Hardware file (preview): il controllo funziona in modalità combinata

29

Local file: il controllo funziona in modalità autonoma e riproduce il contenuto di un file archiviato

sul filesystem del dispositivo

Remote file: il controllo funziona in modalità autonoma e riproduce il contenuto di un flusso

multimediale disponibile in rete

AudioPlayer

Un AudioPlayer è un controllo capace di riprodurre un flusso multimediale di tipo audio. La tabella

seguente descrive le proprietà di questa classe di controlli

sourceType La tipologia di sorgente utilizzata

fileUri Per sorgenti di tipo Local file o Remote file, il percorso, rispettivamente locale e di rete, del file da riprodurre

Tabella 3.17 Campi di AudioPlayer

AudioRecorder

Un AudioRecorder è un controllo capace di acquisire un flusso audio dall’utente attraverso il microfono del

dispositivo e di salvarlo nella memoria persistente del dispositivo stesso, eventualmente inviandolo anche

ad un AudioPlayer. L’unico attributo di questa classe, chiamato audioPlayerId, contiene l’identificativo

univoco del controllo AudioPlayer che riproduce i flussi multimediali acquisiti dal registratore.

ImageView

Un’ImageView è un controllo capace di visualizzare un’immagine. La tabella seguente descrive le proprietà

di questa classe di controlli

sourceType La tipologia di sorgente utilizzata

fileUri Per sorgenti di tipo Local file o Remote file, il percorso, rispettivamente locale e di rete, dell’immagine da visualizzare

Tabella 3.18 Campi di ImageView

PhotocameraController

Un PhotocameraController è un controllo che consiste in un pulsante capace di avviare la fotocamera per

l’acquisizione di una fotografia. L’immagine acquisita viene salvata sul dispositivo ed eventualmente

mostrata in una ImageView collegata. Il pulsante può mostrare due tipologia di contenuto: la scritta camera

o l’icona di una fotocamera. L’implementazione di tale classe di controlli richiede quindi i seguenti attributi

backgroundType Il tipo di contenuto del pulsante

imageViewId L’identificativo unico del controllo ImageView che mostra l’ultima immagine acquisita tramite questo controllo

Tabella 3.19 Campi di PhotocameraController

VideoView

Una VideoView è un controllo capace di riprodurre un video. La tabella seguente descrive le proprietà di

questa classe di controlli

sourceType La tipologia di sorgente utilizzata

fileUri Per sorgenti di tipo Local file o Remote file, il percorso, rispettivamente locale e di rete, del file video da riprodurre

Tabella 3.20 Campi di ImageView

30

VideocameraController

Un VideocameraController è un controllo che consiste in un pulsante capace di avviare la fotocamera per

l’acquisizione di un video. Il video viene salvato sul dispositivo ed eventualmente riprodotto in una

VideoView collegata. Il pulsante può mostrare due tipologia di contenuto: la scritta video o l’icona di una

videocamera. L’implementazione di tale classe di controlli richiede quindi i seguenti attributi

backgroundType Il tipo di contenuto del pulsante

videoViewId L’identificativo unico del controllo VideoView che mostra l’ultimo video acquisito tramite questo controllo

Tabella 3.21 Campi di VideocameraController

3.1.5 Modellazione di controlli che permettono l’inserimento di informazioni

In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo

§3.1.2 per i controlli che presentano un’interazione bidirezionale con l’utente. Tali controlli permettono sia

all’applicazione di mostrare dati all’utente sia all’utente di agire sui dati mostrati, modificandoli. Ciascuno

dei controlli descritti estende la classe UiPhoneControl con attributi propri e caratteristici.

Figura 3.5 Modellazione dei controlli con interazione bidirezionale con l'utente

Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController viene arricchita delle

seguenti relazioni

datePickers Collezione dei controlli di classe DatePicker inseriti nel viewController

editTexts Collezione dei controlli di classe EditText inseriti nel viewController

sliders Collezione dei controlli di classe Slider inseriti nel viewController

spinners Collezione dei controlli di classe Spinner inseriti nel viewController

switches Collezione dei controlli di classe Switch inseriti nel viewController

timePickers Collezione dei controlli di classe TimePicker inseriti nel viewController Tabella 3.22 Estensione di ViewController ai controlli con interazione bidirezionale con l’utente

31

EditText

La classe EditText rappresenta controlli ad interazione bidirezionale di tipo testuale. Ogni controllo di

questo tipo contiene un testo che può essere modificato dall’utente. Se il testo da mostrare non contiene

caratteri, il controllo può apparire vuoto oppure mostrare un messaggio predefinito chiamato placeholder.

Inoltre, è possibile per il designer personalizzare la dimensione e il colore del testo contenuto in questo

controllo. La personalizzazione del colore non riguarda il placeholder, al contrario di quella sulla dimensione

del testo.

L’implementazione di questa tipologia di controlli utente richiede quindi i seguenti attributi

initialContent Il testo contenuto nel controllo prima che l’utente lo modifichi

placeholder Il messaggio placeholder per il controllo

textColor Il colore del testo contenuto nel controllo

textSize La dimensione (in dip) del testo contenuto nel controllo Tabella 3.23 Campi di EditText

Slider

Un controllo di tipo Slider permette all’utente di selezionare un valore da un insieme discreto e opaco di

valori ordinati. Esso si presenta come una barra orizzontale su cui è innestato un cursore: muovendo il

cursore a destra o a sinistra si scorre l’insieme dei valori possibili per il controllo. Tipicamente l’insieme di

base è un intervallo sull’insieme dei numeri naturali. Questo controllo è molto usato anche nelle

funzionalità del sistema operativo: ad esempio, viene utilizzato nelle interfacce di controllo dei dispositivi

audio per la regolazione del volume. Dato il basso livello di personalizzazione disponibile, tale controllo non

ha proprietà caratteristiche.

Spinner

Un controllo di tipo Spinner viene utilizzato per permettere all’utente di selezionare un valore da una lista

di opzioni, in modo analogo ai menu a tendina tipici delle interfacce di ambienti desktop. In questo modello

non è possibile definire la lista delle opzioni o l’opzione selezionata in modo predefinito. Dato il basso

livello di personalizzazione disponibile, tale controllo non ha proprietà caratteristiche.

Switch

Un controllo di tipo Switch viene utilizzato per permettere all’utente di modificare in modo intuitivo un

valore booleano. Il comportamento di tale controllo è analogo a quello di un interruttore a due fasi,

ciascuna delle quali corrisponde ad un valore logico booleano. Il valore predefinito del controllo è false.

Dato il basso livello di personalizzazione disponibile, tale controllo non ha proprietà caratteristiche.

DatePicker e TimePicker

Un controllo di tipo DatePicker permette la visualizzazione e la modifica di una data, mentre un controllo di

tipo TimePicker permette la visualizzazione e la modifica di un orario. Tipicamente si presentano, in

modalità visualizzazione, come una normale casella di testo contenente una data o un orario formattati in

base alle convenzioni locali del dispositivo, mentre in modalità modifica presentano una UI che supporta

l’utente nell’operazione. Tale interfaccia è controllata dal sistema operativo su cui l’app è in esecuzione.

Dato il basso livello di personalizzazione disponibile, tale controllo non ha proprietà caratteristiche.

3.1.6 Modellazione di controlli con ruoli di navigazione

In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo

§3.1.2 per i controlli che implementano funzionalità di navigazione all’interno dell’applicazione.

32

Questo modello contiene due tipologie di azioni di navigazione: la navigazione all’interno di una scena e la

navigazione tra scene diverse. Una scena può essere composta da più viewController, ma nella maggior

parte dei casi questi non sono tutti simultaneamente visibili sullo schermo. Nel caso specifico di una scena

di tipo SINGLE_VC_TAB (si veda il paragrafo §3.1.1) solo una vista è visibile in un dato istante, ma è possibile

cambiarla tramite l’apposito menù. Nel caso invece di una scena di tipo SINGLE_VC la navigazione a schede

non è presente, ed è quindi necessario introdurre la possibilità di implementare un meccanismo di

passaggio da una vista ad un’altra. Tale meccanismo di navigazione interno ad una scena può essere

implementato assegnando ad appositi controlli azioni di navigazione aventi come destinazione un

viewController appartenente alla scena da cui l’azione origina. È possibile assegnare a tali controlli

un’azione diversa per ogni contesto di partenza. In altre parole, è possibile assegnare a tali controlli un

viewController di destinazione diverso per ogni scena in cui viene incluso il viewController a cui il controllo

appartiene. La navigazione tra scene diverse può essere implementata nel medesimo modo, se l’azione di

navigazione origina da un controllo utente. La navigazione tra scene tuttavia deve essere resa possibile

anche nel caso in cui non ci sia un contesto di partenza: è il caso del menù dell’applicazione, che consente

di passare a qualunque scena indipendentemente dal contesto di partenza.

Il seguente diagramma mostra le parti del modello dedicate ad implementare tali meccanismi di

navigazione.

Figura 3.6 Modellazione dei controlli relativi alla navigazione

Oltre al Menu, i controlli che hanno un ruolo nella navigazione all’interno dell’applicazione sono tre: Button,

ListView e GridView. Il diagramma mostra infatti come queste entità contengano uno o più oggetti che

descrivono azioni di navigazione (istanze di Navigation). Tutte le altre entità introdotte in questo

diagramma sono entità accessorie al funzionamento dei controlli di navigazione, ma non hanno un ruolo di

primo piano per quanto riguarda la navigazione stessa.

33

Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController viene arricchita delle

seguenti relazioni

gridViews Collezione dei controlli di classe GridView inseriti nel viewController

listViews Collezione dei controlli di classe ListView inseriti nel viewController

buttons Collezione dei controlli di classe Button inseriti nel viewController Tabella 3.24 Estensione di ViewController ai controlli con ruolo nella navigazione

Anche la classe Application viene arricchita di una relazione

menu Collezione dei controlli di classe DatePicker inseriti nel viewController Tabella 3.25 Estensione di ViewController ai controlli con ruolo nella navigazione

Nel seguito di questo capitolo descriveremo prima la modellazione dell’azione di navigazione, quindi i vari

controlli che hanno capacità di navigazione. Gli elementi accessori verranno trattati insieme agli elementi a

cui sono collegati.

Navigation

La classe Navigation descrive un’azione di navigazione all’interno dell’applicazione. Il modello di

navigazione adottato è molto semplice, e consente la sola definizione della destinazione, dato il contesto di

partenza. Nonostante sia molto semplice, questo modello permette la gestione di diversi casi

La navigazione a partire da un elemento del menu principale non è caratterizzata da un contesto di

partenza, ma solo da un elemento di arrivo, e tale elemento è sempre una scena

La navigazione a partire da un controllo utente ha sempre un contesto di partenza costituito dal

viewController in cui il controllo è inserito e soprattutto dalla scena di cui il viewController è parte

nel momento in cui origina l’azione di navigazione. Le destinazioni possibili possono essere due: un

altro viewController della stessa scena o una scena diversa

L’implementazione riportata nel diagramma in figura 3.6 permette di gestire tutti questi casi. La seguente

tabella descrive il ruolo dei vari attributi della classe.

id Identificativo univoco dell’azione di navigazione (otto caratteri alfanumerici)

contextScene Riferimento alla scena da cui origina l’azione di navigazione. Ha valore nullo in caso di navigazione a partire dal menu principale

destinationScene La scena di destinazione dell’azione di navigazione. Se la destinazione è un viewController all’interno della scena associata a contextScene, questo attributo ha valore nullo

destinationViewController Riferimento al viewController all’interno della scena associata a contextScene che costituisce la destinazione della navigazione. Se l’azione di navigazione ha come destinazione una scena, questo attributo assume valore nullo

Tabella 3.26 Campi di Navigation

Menu e MenuItem

La classe Menu descrive il menu principale dell’applicazione. In questo modello il menù è ridotto ad una

collezione di oggetti di classe MenuItem, ciascuno dei quali rappresenta una voce all’interno del menu

principale. Ogni elemento del menù è caratterizzato da un titolo descrittivo e contiene una sola azione di

navigazione, al fine di evitare ambiguità nella scelta della destinazione di navigazione.

Le seguenti tabelle descrivono i campi di Menu e MenuItem necessari alla loro descrizione

34

menuItems Lista ordinata degli elementi da includere nel menù dell’applicazione. Nel menù applicativo, gli elementi sono mostrati nell’ordine definito dalla lista

Tabella 3.27 Campi di Menu

id Identificativo univoco dell’elemento del menù (stringa alfanumerica da otto caratteri)

title Il titolo dell’elemento di menù, ovvero il testo descrittivo di questo elemento che appare all’utente quando apre il menù

navigation L’azione di navigazione da eseguire quando l’utente seleziona questo elemento dal menù

Tabella 3.28 Campi di MenuItem

Button

Un elemento di classe Button rappresenta un pulsante che l’utente può cliccare. Un pulsante è un controllo

utente, e in quanto tale estende la classe UiPhoneControl.

Un pulsante è caratterizzato dal testo in esso contenuto, che ne descrive la funzionalità all’interno

dell’applicazione, e da vari parametri di personalizzazione stilistica. In questo modello è infatti possibile

definire il raggio con il quale vengono arrotondati i bordi del pulsante e modificare il colore del testo in esso

contenuto. È inoltre possibile modificare il colore di sfondo del pulsante e definire un colore che ne prende

temporaneamente il posto durante la pressione da parte dell’utente. Il valore predefinito per

l’arrotondamento dei bordi è 2 dip, il colore predefinito del testo è il nero, mentre i colori di sfondo e di

pressione sono dipendenti dal sistema operativo.

È possibile associare ad ogni pulsante una azione di navigazione per ciascuna scena in cui viene istanziato il

viewController a cui il pulsante appartiene. Ogni pulsante quindi possiede una collezione di azioni di

navigazione, che globalmente implementano una funzione di navigazione parametrica rispetto alla scena di

origine della navigazione stessa.

La descrizione di un pulsante richiede quindi i seguenti parametri particolari

title Il testo descrittivo contenuto nel pulsante

textColor Il colore nello spazio RGB con cui viene reso il testo contenuto in title

backgroundColor Il colore di sfondo del pulsante

clickColor Il colore di sfondo del pulsante durante la pressione da parte dell’utente

borderRadius Il raggio in base al quale sono arrotondati gli angoli del pulsante. Se ha valore 0, gli angoli sono angoli retti non arrotondati.

navigations Collezione di azioni di navigazione associate al pulsante Tabella 3.29 Campi di Button

ListView e ListViewCell

Un controllo di classe ListView rappresenta una lista di elementi, ciascuno di classe ListViewCell. La classe

ListView estende la classe comune a tutti i controlli utente: UiPhoneControl.

È interessante definire il controllo ListView come controllo di navigazione in quando è possibile assegnare

alla lista stessa un’azione di navigazione che viene eseguita quando un elemento della lista viene

selezionato. Questa soluzione è congegnale all’implementazione di pattern di navigazione come il noto

Master – Detail che viene utilizzato in qualsiasi applicazione di messaggistica: il lato master è costituito da

una lista di elementi, ciascuno dei quali rappresenta nell’esempio una conversazione, e il lato detail è

costituito dalla vista che mostra effettivamente i messaggi inviati e ricevuti. Le modalità di navigazione di

35

un pattern Master – Detail sono generalmente note all’utente, e quindi è interessante poterle modellare in

questo lavoro. Come per i pulsanti (Button) è possibile definire una azione di navigazione per ogni scena in

cui viene inserito il viewController a cui la lista appartiene. Tali azioni nel loro complesso possono essere

interpretate come una funzione di navigazione parametrica rispetto alla scena da cui origina la navigazione

stessa.

È possibile assegnare un colore di sfondo alla lista, che resta visibile dietro al contenuto di ciascuna cella.

Un oggetto della classe ListViewCell non rappresenta un controllo utente. Tale classe è infatti strumentale

alla definizione del controllo ListView. Un oggetto di classe ListViewCell rappresenta un elemento della lista.

Ci sono tre possibili tipologie di liste, che si distinguono tra loro per la ricchezza di informazioni contenute

nelle celle che le compongono. Il layout di una cella è quindi determinato dalla tipologia di lista a cui essa

appartiene. Le possibili tipologie di lista sono

Simple: le celle contengono del semplice testo

Image: le celle contengono un’immagine e un testo descrittivo

Detailed: le celle contengono un’immagine, un titolo e un sottotitolo

Nel modello attuale non è possibile personalizzare l’immagine associata ad una cella. Il modello è

comunque facilmente estendibile in questo senso.

L’implementazione del controllo ListView e della classe accessoria ListViewCell richiedono i seguenti

attributi

backgroundColor Il colore di sfondo della lista

listType Il tipo di lista, che definisce il layout delle celle ad essa appartenenti

navigations Collezione delle azioni di navigazione associate al controllo ListView Tabella 3.30 Campi di ListView

title Titolo della cella

subtitle Sottotitolo della cella Tabella 3.31 Campi di ListViewCell

GridView e GridViewCell

Un controllo di classe GridView rappresenta una griglia di elementi, ciascuno di classe GridViewCell. La

classe GridView estende la classe comune a tutti i controlli utente: UiPhoneControl.

È interessante definire il controllo GridView come controllo di navigazione in quando è possibile assegnare

alla griglia stessa un’azione di navigazione che viene eseguita quando un elemento della lista viene

selezionato. Questa soluzione è congegnale all’implementazione di pattern di navigazione come il noto

Master – Detail che viene utilizzato in qualsiasi applicazione che contiene una galleria fotografica: il lato

master è costituito da una griglia di elementi, ciascuno dei quali contiene un’anteprima dell’immagine, e il

lato detail è costituito dalla vista che mostra effettivamente l’immagine a tutto schermo. Le modalità di

navigazione di un pattern Master – Detail sono generalmente note all’utente, e quindi è interessante

poterle modellare in questo lavoro. Come per i pulsanti (Button) è possibile definire una azione di

navigazione per ogni scena in cui viene inserito il viewController a cui la lista appartiene. Tali azioni nel loro

complesso possono essere interpretate come una funzione di navigazione parametrica rispetto alla scena

da cui origina la navigazione stessa.

36

Un oggetto della classe GridViewCell non rappresenta un controllo utente. Tale classe è infatti strumentale

alla definizione del controllo GridView. Un oggetto di classe GridViewCell rappresenta una cella della griglia

a cui appartiene. Ci sono tre possibili tipologie di griglie, che si distinguono tra loro per la ricchezza di

informazioni contenute nelle celle che le compongono. Il layout di una cella è quindi determinato dalla

tipologia di griglia a cui essa appartiene. Le possibili tipologie di griglia sono

Simple: le celle contengono del semplice testo

Image: le celle contengono un’immagine

Detailed: le celle contengono un’immagine e una didascalia

Nel modello attuale non è possibile personalizzare l’immagine associata ad una cella. Il modello è

comunque facilmente estendibile in questo senso.

L’implementazione del controllo GridView e della classe accessoria GridViewCell richiedono i seguenti

attributi

gridType Il tipo di lista, che definisce il layout delle celle ad essa appartenenti

navigations Collezione delle azioni di navigazione associate al controllo GridView Tabella 3.32 Campi di ListView

title Il testo contenuto nella cella, che viene mostrato come didascalia se la griglia è di tipo detailed

Tabella 3.33 Campi di ListViewCell

3.1.7 Modellazione di altri elementi

La modellazione della componente View può essere arricchita di ulteriori elementi che non appartengono

alla classe dei controlli utente, ma che possono comunque risultare molto utili alla buona riuscita di

un’applicazione. Tali elementi sono:

alertDialogs: finestre di dialogo popup che informano l’utente in merito a qualche evento nel

dominio dell’applicazione. Dato che essi appaiono sempre in primo piano ed in posizione centrale

sullo schermo, sono utilizzati per fornire informazioni urgenti e/o eccezionali

progressDialog: finestre di dialogo popup utilizzate per informare di una qualche attesa. Il tipo e la

durata dell’attesa dipendono dallo specifico caso di utilizzo

asyncTasks: attività che vengono eseguite in background. Lo scopo specifico dell’attività dipende

dallo specifico caso d’uso, ma in generale un’attività di sfondo viene utilizzata per svolgere compiti

di lunga durata: la loro esecuzione nel thread principale infatti renderebbe l’interfaccia grafica poco

reattiva nei confronti dell’utente

Il seguente diagramma mostra come questi elementi sono inclusi nel modello

37

Figura 3.7 Modellazione di elementi aggiuntivi della vista

L’inserimento di tali elementi richiede che alla classe ViewController vengano aggiunte le seguenti relazioni

asyncTasks Collezione delle attività in background disponibili all’interno del viewController

alertDialogs Collezione delle finestre di dialogo che possono essere aperte nel contesto del viewController

prograssDialogs Collezione delle finestre di attesa che possono essere utilizzate nel contesto del viewController

Tabella 3.34 Estensione di ViewController agli elementi accessori

AsyncTask

Un oggetto della classe AsyncTask descrive un’attività in background all’interno dell’applicazione. Data la

variabilità degli scopi che possono essere assegnati ad un’attività, questo modello non permette la

modellazione del compito eseguito, ma soltanto dell’esistenza di tale compito. In tal modo, è possibile per il

modellatore descrivere quante attività dovranno essere implementate e denominarle individualmente, e,

quindi, è possibile generare automaticamente tutta la struttura necessaria alla loro creazione e messa in

esecuzione. Il team di implementazione dovrà solamente curare l’algoritmo attualmente eseguito, senza

doversi occupare delle condizioni di contorno. Il modello di un’attività in background è quindi molto

semplice e contiene una sola proprietà: il campo name che contiene il nome distintivo dell’attività in

background.

ProgressDialog

Un oggetto della classe ProgressDialog descrive un messaggio di attesa che viene mostrato all’utente in

particolari condizioni. La generalità di tali condizioni richiederebbe una modellazione eccessivamente

complessa per gli scopi di questo lavoro, che si limita, quindi, a permettere la modellazione dell’aspetto del

messaggio. Un messaggio di attesa è caratterizzato da un titolo e da un breve testo che hanno lo scopo di

informare l’utente in merito al motivo dell’attesa stessa, e dal classico elemento animato presente, con

varie grafiche, su ogni piattaforma con interfaccia grafica.

Il modello quindi permette la definizione delle seguenti proprietà

38

id Identificativo univoco della finestra di attesa (stringa alfanumerica di otto caratteri)

title Il titolo della finestra

message Il messaggio informativo contenuto nella finestra

spinner Se è falso, l’elemento animato di attesa viene omesso Tabella 3.35 Campi di ProgressDialog

AlertDialog

Un oggetto della classe AlertDialog modella un messaggio che l’applicazione può inviare all’utente al

verificarsi di un particolare evento. Questo modello si concentra sull’aspetto grafico di tale messaggio e non

sulla descrizione dell’evento che lo genera, dato che la complessità della descrizione introdurrebbe più

problemi che benefici. Un alertDialog è quindi completamente descritto da un titolo e dal messaggio che

esso porta all’utente. La sua implementazione consiste quindi dei seguenti campi

id Identificativo univoco della finestra di dialogo (stringa alfanumerica di otto caratteri)

title Il titolo del messaggio

message Il messaggio da inviare all’utente Tabella 3.36 Campi di AlertDialog

3.2 Modello del Model La componente Model di un’applicazione si occupa della gestione dei dati di interesse per l’applicazione

stessa, i quali sono contenuti in una struttura rappresentativa del dominio applicativo. La modellazione

della componente Model permette la definizione di tale struttura e di una logica per la persistenza della

stessa. Il collegamento diretto tra la struttura del modello e la logica di persistenza rende possibile il

salvataggio automatico delle modifiche ai dati, feature questa tradizionalmente presente nella maggior

parte della applicazioni mobili.

Questo modello consente di lavorare con quattro strategie di persistenza

SharedPreferences permette l’archiviazione di dati come coppie chiave – valore.

File Storage permette l’archiviazione di dati come contenuto di files memorizzati nel file system del

dispositivo

Relational database permette l’archiviazione di dati tramite un database relazionale

Cloud database permette l’archiviazione di dati tramite il database non relazionale fornito da

Firebase, chiamato Firebase Realtime Database

È possibile suddividere il modello su varie strategie di persistenza, selezionando per ogni elemento del

Model la strategia di persistenza più adatta: oggetti di tipo testuale di discreta lunghezza possono essere

archiviati facilmente su files; il nome dell’utente dell’applicazione trova la sua collocazione ideale in una

chiave nel dizionario delle SharedPreferences; dati strutturati e fortemente interconnessi possono essere

archiviati facilmente nel database relazionale; dati che devono essere sincronizzati tra più dispositivi

possono essere archiviati nel database in cloud.

La seguente figura 3.8 mostra la struttura del modello del Model.

39

Figura 3.8 Modello della componente Model

È immediatamente evidente come il modello sia ripartito in quattro sezioni, ciascuna corrispondente ad

una diversa strategia di persistenza. La classe DataHandler è la radice del modello del Model e possiede una

visione d’insieme delle strategie di persistenza su cui il Model è distribuito. Ciascuno degli Handler che

possono essere inseriti nel DataHandler corrisponde ad una strategia di persistenza. Ogni Handler contiene

al suo interno oggetti che definiscono la struttura del modello memorizzata tramite la strategia ad esso

associata.

Nel seguito di questo capitolo andremo ad analizzare ciascuna tipologia di persistenza.

3.2.1 Archiviazione tramite database relazionale

I dati archiviati nel database relazionale sono strutturati secondo il tradizionale modello Entità – Relazione.

Per ogni entità, il modello consente la descrizione di attributi e relazioni verso altre entità. Ciascun attributo

è caratterizzato da un nome e un tipo, a scelta tra i classici tipi disponibili in tutti i DBMS relazionali:

string: rappresenta un dato di tipo testuale di lunghezza arbitraria

int: rappresenta un numero intero con segno

bool: rappresenta un valore logico booleano (vero o falso)

date: rappresenta una data

float: rappresenta un numero decimale a singola precisione

double: rappresenta un numero decimale a doppia precisione

40

Per quanto riguarda le relazioni, esse sono descritte, oltre che dall’entità che le possiede, dall’entità

collegata e dalla cardinalità della relazione. Le cardinalità possibili sono

1:1: ad ogni istanza dell’entità che possiede la relazione corrisponde un’istanza dell’entità collegata

1:N: ad ogni istanza dell’entità che possiede la relazione corrispondono 0 o più istanze dell’entità

collegata

N:1: ad ogni istanza dell’entità collegata corrispondono 0 o più istanze dell’entità che possiede la

relazione

Una relazione di cardinalità N:N non può essere immediatamente inserita, ma è comunque modellabile

attraverso l’ausilio di un’entità di corrispondenza. Date due entità A e B e una relazione r tra A e B con

cardinalità N:N, è possibile decomporre tale relazione in due relazioni s e t attraverso l’introduzione di una

entità C di appoggio: ogni istanza di C corrisponde all’associazione tra un’istanza di A e una di B, ed è quindi

ottenuta dalla concatenazione delle chiavi primarie delle due entità coinvolte. La relazione s tra A e C e la

relazione t tra B e C hanno entrambe cardinalità 1:N e possono quindi essere espresse nel modello sopra

descritto.

La descrizione di una entità (espressa dalla classe Entity) richiede quindi le seguenti proprietà

name Il nome distintivo dell’entità

primaryKey Il nome dell’attributo che svolge il ruolo di chiave primaria nella relazione. Il tipo di tale attributo è string.

entityAttributes Collezione di attributi assegnati all’entità

entityRelations Collezione di relazioni che originano dall’entità Tabella 3.37 Campi di Entity

La descrizione di un attributo (espressa dalla classe EntityAttribute) richiede le seguenti proprietà

name Il nome distintivo dell’attributo

type Il tipo del dato memorizzato nell’attributo Tabella 3.38 Campi di EntityAttribute

La descrizione di una relazione (espressa dalla classe EntityRelation) richiede le seguenti proprietà

name Il nome distintivo della relazione

destination Il nome distintivo dell’entità di destinazione della relazione

type La cardinalità della relazione (espressa rispetto all’entità che possiede la relazione stessa)

Tabella 3.39 Campi di EntityRelation

3.2.2 Archiviazione tramite files

I dati archiviati tramite files in questo modello sono implicitamente di tipo testuale. Questo modello di

archiviazione permette di specificare un numero arbitrario di files, ciascuno caratterizzato da un nome

distintivo e dalla relativa estensione. È inoltre possibile marcare un file come temporaneo: questo autorizza

il sistema operativo ad eliminare il file qualora sia richiesta la liberazione di spazio su disco.

La descrizione della parte di modello archiviata tramite files è molto semplice: la classe StorageHandler

contiene una collezione di descrittori di files, ciascuno istanza della classe StorageRecord. Ogni

storageRecord descrive un file in cui un elemento del modello è archiviato. Le proprietà di uno

storageRecord sono descritte nella seguente tabella

41

name Il nome distintivo del file all’interno del filesystem

extension L’estensione associata al nome del file

tempFile Marcatura che distingue i files temporanei se ha valore di verità true.

Tabella 3.40 Campi di StorageRecord

3.2.3 Archiviazione tramite SharedPreferences

L’archiviazione tramite SharedPreferences si basa sull’utilizzo di un dizionario tipizzato a livello di record.

Ogni dato è associato ad una specifica chiave, come in ogni dizionario, ed ha un proprio tipo.

Potenzialmente, ogni chiave può avere associato un dato di tipo diverso da tutti gli altri. I tipi di dato

possibili sono

string: dati di tipo testuale di lunghezza non predefinita

int: dati di tipo intero con segno (32 bit)

long: dati di tipo intero con segno (64 bit)

boolean: dati di tipo logico booleano (logica a due valori vero e falso)

float: numero decimale a singola precisione

double: numero decimale a precisione doppia

La classe PreferenceRecord consente la definizione delle varie chiavi del dizionario e del relativo tipo di

dato, necessitando quindi delle seguenti proprietà

key La chiave da inserire nel dizionario

value Il valore predefinito associato alla chiave

type Il tipo del valore associato alla chiave Tabella 3.41 Campi di PreferenceRecord

La classe PreferenceHandler raggruppa tutte le chiavi definite per le SharedPreferences, ovvero tutte le

istanze di PreferenceRecord.

3.2.4 Archiviazione tramite database cloud

Il modello utilizzato in questo lavoro di tesi consente di descrivere la forma di un database cloud in grado di

memorizzare i dati di interesse per l’applicazione.

Per capire la logica catturata dal modello, è necessario descrivere la struttura del database cloud

considerato in questo lavoro: Firebase Realtime Database. Al suo interno, i dati sono archiviati in formato

JSON. L’intero database consiste di un solo oggetto JavaScript, codificato in JSON per l’archiviazione. È

possibile memorizzare oggetti complessi e liste tramite nesting: ad una chiave dell’oggetto globale viene

assegnato un oggetto, le cui chiavi possono ricorsivamente contenere oggetti. Questa modalità definisce

quindi un’archiviazione basata su un albero in cui ciascuna foglia è un valore elementare, ciascun nodo

intermedio è un oggetto complesso e ciascun ramo è etichettato con la chiave con cui nel nodo padre è

memorizzato l’oggetto contenuto nel nodo figlio. Questa struttura induce un accesso ai dati basato su

referenze: una referenza rappresenta il percorso del dato che si vuole accedere a partire dalla radice

dell’albero.

Il modello per l’archiviazione cloud si basa sulla struttura dei dati nel database cloud stesso: il componente

CloudHandler raccoglie una collezione di oggetti di tipo CloudObject, ciascuno dei quali descrive la forma di

un oggetto memorizzato nel database cloud. Ogni oggetto di questo tipo può contenere attributi di diverso

tipo. I tipi disponibili sono

42

string: il dato contenuto nell’attributo è di tipo testuale

int: il dato contenuto nell’attributo è di tipo intero (con segno)

float: il dato contenuto nell’attributo è un numero decimale (singola precisione)

double: il dato contenuto nell’attributo è un numero decimale (doppia precisione)

ref: il dato contenuto nell’attributo è un oggetto complesso

ref_list: il dato contenuto nell’attributo è una lista di oggetti complessi omogenei

Un attributo il cui tipo sia ref o ref_list deve necessariamente contenere un riferimento alla struttura

dell’oggetto complesso memorizzato, singolarmente o come lista, al suo interno. I tipi ref e ref_list

consentono così di realizzare il nesting sopra descritto.

All’interno della collezione di oggetti posseduta dal CloudHandler vi è sempre un oggetto chiamato

RootObject che costituisce il punto di ingresso della struttura: tale oggetto infatti è il modello dell’oggetto

JSON globale archiviato nel database cloud. Attraverso la definizione di attributi di tipo ref o ref_list

sull’oggetto RootObject è possibile introdurre un primo livello di nesting. I successivi livelli possono essere

definiti ricorsivamente in modo analogo. Ogni oggetto appartenente al CloudHandler diverso dal

RootObject, per essere accessibile, deve essere collegato ad un altro oggetto accessibile o al RootObject per

mezzo di un attributo di tipo ref o ref_list.

Per descrivere l’intera struttura sono quindi necessarie tre classi: CloudHandler, che mantiene la collezione

di tutti gli oggetti cloud definiti per l’applicazione, CloudObject, che rappresenta un oggetto caratterizzato

da una collezione di attributi, e ObjectAttribute che rappresenta uno di tali attributi. Le seguenti tabelle

dettagliano il ruolo delle proprietà di CloudObject e ObjectAttribute

name Il nome distintivo dell’oggetto cloud.

attributes La collezione degli attributi dell’oggetto Tabella 3.42 Campi di CloudObject

name Il nome distintivo dell’attributo. È anche la chiave con cui tale attributo è memorizzato nell’oggetto padre

type Il tipo dell’attributo

object Se l’attributo ha tipo ref o ref_list, questa proprietà contiene il nome distintivo dell’oggetto archiviato in questo attributo, altrimenti ha valore nullo

Tabella 3.43 Campi di ObjectAttribute

3.3 Modello del Controller Il ruolo del Controller all’interno di una applicazione MVC è quello di tradurre gli eventi della View in azioni

di modifica del Model. Non essendo possibile catturare in modello la complessità di tale definizione, questo

lavoro di tesi si concentra su una tra tutte le possibili implicazioni: il ruolo che assume il controller

nell’interazione (indiretta) tra la View e il modello persistente. Dato che non è possibile, per

un’applicazione MVC, lavorare direttamente con il modello persistente (che non è osservabile), è

necessario mantenere una copia in memoria degli elementi su cui l’applicazione va ad operare. Questo

introduce la necessità di una politica di mantenimento della coerenza tra i due “modelli” e di strumenti per

importare dati dal modello persistente al modello di lavoro. Il Controller deve quindi operare su due fronti

Quando una scena viene istanziata per essere mostrata all’utente, il Controller carica i dati richiesti

dalla scena stessa dal modello persistente al modello di lavoro, se necessario. È infatti possibile che

43

i dati richiesti siano già presenti nel modello di lavoro per effetto di operazioni di caricamento

precedenti

Quando all’interno di una scena si verifica un evento di modifica di un dato, il Controller aggiorna

immediatamente il modello di lavoro, in modo che la View possa mostrare all’utente i dati

aggiornati, e aggiorna anche il modello persistente in modo da mantenere la coerenza tra modello

di lavoro e modello persistente.

Tuttavia, questi due ruoli si differenziano solamente per il tipo di operazione che svolgono sul modello

persistente: il primo di lettura e il secondo di scrittura. In entrambi i casi il modello di lavoro è oggetto di

un’operazione di scrittura e l’azione scatenante l’operazione è un evento della vista: nel primo caso un

evento di transizione ad una scena, nel secondo caso un interazione dell’utente con la vista.

La condizione necessaria per la realizzazione di questo tipo di Controller è la possibilità di descrivere di quali

dati ciascuna vista necessiti, e più nel dettagli di quali dati ogni controllo necessiti. Tale requisito è infatti

strumentale alla realizzazione del secondo ruolo del controller: gli eventi di modifica del Model infatti

provengono principalmente da controlli ad interazione bidirezionale con l’utente (paragrafo §3.1.5).

È possibile suddividere quest’ultimo problema in due problemi più circoscritti. Il primo problema riguarda il

rapporto tra il Model e una scena. Ciascuna scena infatti non necessita generalmente dell’accesso all’intero

modello, ma solo ad una sua parte. Gli elementi del modello che vengono effettivamente utilizzati dalla

scena formano il modello della scena, o scene model. Il scene model è, quindi, un sottoinsieme delle

informazioni contenute nel Model. Il secondo problema riguarda il rapporto tra una scena e i controlli

contenuti nei viewController che la compongono. La descrizione di quali dati sono utilizzati da ogni

controllo rende possibile la generazione automatica del codice necessario a fornire tali dati e a gestire gli

eventi prodotti dai controlli stessi. Grazie all’introduzione del scene model, tale descrizione può essere

semplificata: è possibile definire, per ogni controllo, una o più proprietà capaci di assumere valori derivati

dal scene model, e definire per tali proprietà una politica di derivazione del valore stesso. Tali proprietà

sono definite proprietà collegabili. Come per la navigazione (§3.1.6), questa descrizione può essere infine

resa parametrica rispetto alla scena in cui il controllo viene mostrato. In questo lavoro di tesi verrà descritta

solamente la derivazione diretta parziale, ma è possibile estendere questo approccio ad altre politiche. La

politica di derivazione diretta prevede che ogni proprietà collegabile possa assumere un valore elementare

direttamente disponibile nel scene model. Tale valore può quindi essere solamente:

Un record del dizionario delle SharedPreferences la cui chiave sia nota a design time

L’intero contenuto di un file

Un attributo di un’entità del database relazionale (non una relazione)

Un attributo di tipo elementare (ovvero non ref o ref_list) di un’oggetto cloud

Sebbene sia piuttosto restrittiva, la derivazione diretta copre un buon numero di casi d’uso: le operazioni di

inserimento o di modifica di dati complessi sono infatti sempre realizzate come composizione di operazioni

di modifica di dati elementari, a vantaggio di un riduzione della complessità dell’operazione per l’utente

dell’applicazione. La maggiore limitazione della derivazione diretta risiede nell’impossibilità di produrre un

valore aggregato per una proprietà collegabile. Al fine di ridurre la portata di tale limitazione, è possibile

rilassare la derivazione diretta introducendo la possibilità di collegare ad una proprietà anche un oggetto

complesso o una lista di oggetti. Tale collegamento può essere sfruttato per generare automaticamente il

codice per il caricamento nel scene model di tutti gli elementi necessari alla computazione del dato

aggregato, lasciando poi l’implementazione effettiva della logica di calcolo ad un programmatore che

44

interviene sul codice generato a partire dal modello. Questo rilassamento della derivazione diretta è

chiamato derivazione diretta parziale.

La seguente figura 3.9 mostra gli strumenti necessari a descrivere il scene model e la derivazione diretta

parziale.

Figura 3.9 Modellazione del Controller

La figura 3.9 introduce due nuove entità all’interno del modello: AdapterBinding e ModelConnector.

AdapterBinding viene utilizzata per la descrizione del scene model, mentre ModelConnector viene utilizzata

per la descrizione della derivazione diretta parziale.

Al fine di collegare il controller alle altre componenti, è necessario estendere le classi Scene e

UiPhoneControl con le seguenti relazioni:

Alla classe Scene viene aggiunta la relazione adapterBindings che contiene una collezione di

elementi di classe AdapterBinding. Tale collezione, nel suo complesso, descrive il scene model.

Alla classe UiPhoneControl viene aggiunta la relazione modelConnectors, che contiene una

collezione di elementi di classe ModelConnector. Tale collezione, nel suo complesso, descrive la

derivazione diretta parziale dei dati necessari al componente in modo parametrico rispetto alla

scena in cui tale componente è inserito.

Nei capitoli seguenti andremo ad analizzare come le due entità introdotte consentano di realizzare la

descrizione data del Controller.

3.3.1 AdapterBinding

La classe AdapterBinding ha il compito di descrivere la dipendenza di una scena da un elemento del Model.

Dato che il Model è strutturato, in prima analisi, a partire dai diversi meccanismi di persistenza, la classe

AdapterBinding deve implementare la capacità di poter connettere ad una scena dati da qualsiasi archivio

45

persistente in modo trasparente alla scena. Questo richiede che la tipologia del meccanismo di persistenza

sia incapsulata all’interno della classe. Gli attributi e le relazioni di AdapterBinding sono quindi pensati per

realizzare questo incapsulamento.

L’attributo id contiene sempre una stringa univoca alfanumerica di 8 caratteri che costituisce un

identificativo per ciascuna istanza.

L’attributo isList indica se una istanza introduce nel scene model un elemento scalare o una collezione di

elementi. Questo attributo è utile soprattutto quando si lavora con le relazioni in un database relazionale:

introducendo un adapterBinding con il parametro isList impostato a true è possibile descrivere la presenza

di una lista di entità omogenee in tipo all’interno del scene model. Una lista così costruita può servire ad

includere nel scene model le entità collegate ad una entità data attraverso una relazione di tipo 1:N

(§3.2.1).

L’attributo name contiene un nome distintivo dell’adapterBinding. Il nome serve al designer per poi

riconoscere un adapterBinding quando lo utilizza per generare uno o più modelConnector (si veda il

capitolo successivo).

L’attributo type indica il meccanismo di persistenza da cui l’adapterBinding ottiene i dati da importare nel

scene model. Questo attributo può assumere i seguenti valori

Valore Meccanismo di persistenza

file Archiviazione tramite file

preference Dizionario delle SharedPreferences

entity Database relazionale

cloud Database cloud Tabella 3.44 Tipologie di AdapterBinding

Le relazioni file, preference, object e entity assumono tutte valore nullo tranne la relazione corrispondente

al valore dell’attributo type, che contiene la descrizione del valore inserito nel scene model

dall’adapterBinding. In particolare:

Se type contiene il valore file, la relazione file contiene il descrittore del file (§3.2.2) il cui contenuto

viene importato nel scene model con il nome definito dall’attributo name.

Se type contiene il valore preference, la relazione preference contiene il descrittore del record nel

dizionario delle SharedPreferences (§3.2.3 il cui valore viene importato nel scene model con il nome

definito dall’attributo name.

Se type contiene il valore entity, la relazione entity contiene il descrittore dell’entità relazionale

(§3.2.1) di cui una o più istanze (in base al valore dell’attributo isList) vengono importate nel scene

model con il nome definito dall’attributo name.

Se type contiene il valore cloud, la relazione object contiene il descrittore dell’oggetto cloud

(§3.2.4) che viene importato nel scene model con il nome definito dall’attributo name. Se

l’attributo isList ha valore vero nel scene model viene importata una lista di oggetti omogenei e di

tipo definito dal valore della relazione object. Se invece l’attributo isList ha valore falso nel modello

viene importato un singolo oggetto di tipo definito dal valore della relazione object.

Tra le varie tipologie di adapterBinding, la più complessa è quella relativa al database relazionale, dato che

l’estrazione di dati da un database relazionale richiede una query SQL, che in alcuni casi è semplice e

generabile automaticamente (si pensi ad esempio all’estrazione di un’entità dato il valore della sua chiave

46

primaria), mentre in altri richiede un intervento da parte di un programmatore. Dato che l’introduzione di

un meccanismo di modellazione di una generica query SQL renderebbe il modello molto più vicino ad

un’implementazione che ad un prototipo, si è preferito lasciare tali casi all’intervento di un programmatore,

fornendo tuttavia gli strumenti per ridurre al minimo l’intervento dello stesso.

In caso di adapterBinding di tipo preference o file la descrizione dell’elemento da importare nel scene

model è esaustiva rispetto alle informazioni richieste per il caricamento dalla persistenza del dato stesso.

Infatti, nel momento in cui si seleziona un file da importare nel scene model sono noti nome ed estensione,

e non sono necessari altri elementi per accedere al file e leggerne il contenuto. Il medesimo concetto vale

per le SharedPreferences, la specifica di un record da aggiungere al scene model si basa sulla specifica della

sua chiave, che è anche l’unica informazione necessaria ad accedere al record.

In caso di adapterBinding di tipo cloud, è possibile completare la descrizione dell’adapterBinding fornendo

l’attributo cloudRefPath che contiene la referenza dei dati da caricare. In questo modo l’adapterBinding

contiene sia la descrizione del tipo dei dati (tramite la relazione object) sia tutte le informazioni necessarie

ad importare i dati dalla cloud (la referenza all’oggetto, come descritto in §3.2.4).

Infine in caso di adapterBinding di tipo entity, per le considerazioni fatte precedentemente, non è possibile

includere tutte le informazioni necessarie al caricamento dei dati nel scene model. La descrizione del tipo

dell’entità da caricare e del fatto che si tratti di una lista o di uno scalare permette sia di generare tutto il

codice necessario al caricamento dalla persistenza di tali oggetti, lasciando come parametro la query

necessaria alla selezione degli stessi, sia tutto il codice necessario ad aggiornamento ed eliminazione di

ciascuna entità, dato che queste operazioni avvengono sempre in modo scalare. La quantità di codice

generato rispetto al codice totale necessario è quindi inferiore per le sole parti relative alla formulazione

della query di selezione.

3.3.2 ModelConnector

La classe ModelConnector descrive la connessione tra una proprietà collegabile di un controllo utente e un

dato contenuto nel scene model. Tale descrizione coinvolge quattro elementi

Il controllo utente coinvolto nella connessione

La proprietà del controllo utente coinvolta nella connessione

L’adapterBinding da cui il dato viene estratto

Per adapterBinding di tipo non elementare, la proprietà dell’oggetto caricato dall’adapterBinding

che deve fornire i dati al controllo

Di tali elementi, il controllo utente coinvolto nella connessione è costituito dall’istanza di UiPhoneControl

che possiede l’istanza di ModelConnector, mentre gli altri elementi sono inseriti nel modelConnector stesso

come attributi o relazioni, come esplicitato nella seguente tabella

id Identificativo univoco dell’istanza di ModelConnector (stringa alfanumerica di 8 caratteri)

adapter Riferimento all’istanza di adapterBinding che fornisce i dati al modelConnector

property Il nome della proprietà collegabile del controllo che possiede il modelConnector

keypath La proprietà dell’oggetto contenuto nell’adapterBinding da cui leggere i valori per la proprietà collegabile

Tabella 3.45 Campi di ModelConnector

47

È possibile distinguere due casi di modelConnector: i connettori semplici e i connettori complessi. Sono

considerati connettori semplici quei connettori che traggono dati da un adapterBinding di tipo preference o

file. Sono invece considerati connettori complessi quei connettori che leggono dati da un adapterBinding di

tipo entity o cloud.

Nei connettori semplici la proprietà keypath ha sempre come valore una stringa vuota, a significare che il

dato contenuto nell’adapterBinding referenziato dalla proprietà adapter è un dato elementare e non

ulteriormente scomponibile.

Nei connettori complessi, keypath contiene il nome della proprietà da associare alla proprietà collegabile.

Nel caso specifico di un’entità relazionale, tale proprietà può essere data da qualsiasi attributo, ma non da

una relazione. Nel caso specifico di oggetti cloud, tale proprietà può essere data da qualsiasi attributo il cui

tipo non sia ref o ref_list. Questo è imposto perché l’accesso ad oggetti complessi, oltre ad essere

problematico nel rapporto con la persistenza in quanto richiede un’operazione asincrona per ogni

puntatore risolto, può dare origine a grafi di dipendenza ciclici la cui risoluzione a livello di codice

applicativo diventa inutilmente complessa. Tale imposizione non risulta comunque limitante: nel momento

in cui si abbia la necessità di accedere ad un oggetto complesso, è possibile creare per esso un nuovo

adapterBinding a livello di scene model e utilizzarlo per definire opportuni modelConnector.

Definiamo il valore di un modelConnector come il valore dell’attributo di nome keypath all’interno

dell’oggetto inserito nel scene model dall’adapter qualora keypath sia non vuoto. Nel caso in cui keypath sia

vuoto, il valore del modelConnector coincide con l’oggetto inserito nel scene model dall’adapter.

L’implementazione della derivazione diretta parziale è realizzata grazie all’assenza di controlli di

compatibilità di tipo tra la proprietà collegabile e il valore del modelConnector. È infatti possibile collegare

ad una qualsiasi proprietà un oggetto complesso o una lista di oggetti complessi senza invalidare il modello,

con la condizione che un programmatore completi il codice generato a partire dal modello. In mancanza di

intervento da parte di un programmatore, il valore del modelController verrà utilizzato come valore della

proprietà collegabile tramite operazioni di typecasting.

La maggior parte dei controlli dispone di proprietà che possono essere associate ad elementi del model. Tali

proprietà sono riportate in questa sezione. Quasi ogni proprietà collegabile ha una controparte

indipendente dal model, che viene utilizzata dall’applicazione generata nel caso in cui il valore dinamico

letto dal model non sia disponibile.

La seguente tabella riporta l’elenco di tutte le proprietà collegabili di ciascun controllo utente disponibile a

livello di View.

48

Tipologia di controlli Proprietà Controparte

AudioPlayer source fileUri

Button title title

Card title title

Card subtitle subtitle

Datepicker value - (*1)

GridView items - (*2)

GridViewCell (*3) title title

ImageView source fileUri

Label title title

ListView items - (*2)

ListViewCell (*3) title title

ListViewCell (*3) subtitle subtitle

Map latitude latitude

Map longitude longitude

Slider value - (*1)

Spinner value - (*1)

Switch value - (*1)

TextEdit value title

Timepicker value - (*1)

VideoView source fileUri

WebView htmlFileName htmlFileName Tabella 3.46 Proprietà collegabili

(*1)

La proprietà non ha una controparte per motivi di retrocompatibilità

(*2)

Le celle di una lista o di una griglia sono generate dinamicamente dalla lista passata tramite la proprietà

items, oppure possono essere definite staticamente. Il trattamento dei due casi è però radicalmente

diverso e la controparte non può essere trovata in una singola proprietà, ma piuttosto in un insieme di

elementi.

(*3)

Questo elemento non è un controllo utente, e in quanto tale non dispone di una relazione con oggetti di

tipo modelConnector. Le proprietà evidenziate sono pertanto definite sul controllo che contiene questo

elemento con il nome prefisso dalla stringa “@cell.”. Un generatore di codice associato a questo modello

deve considerare questo caso e implementare opportunamente il collegamento con tali proprietà. La

definizione di un modelConnector per questo tipo di oggetti sarebbe un errore concettuale: la proprietà

collegabile assume un significato nel momento in cui questo elemento è generato dinamicamente a partire

dalla proprietà items del controllo che lo contiene, e un modelConnector dovrebbe essere definito su una

istanza disegnata staticamente dal modellatore. Tale situazione è paradossale.

49

4 Protocode

4.1 Introduzione Protocode 4.0 è un’applicazione web per lo sviluppo model-driven di applicazioni mobili. Essa consente di

progettare un modello per le componenti View e Model di un’applicazione mobile implementata secondo il

pattern architetturale MVC.

Il progetto, nato da un lavoro di tesi di Mattia Natali nel 2013 [4], è stato successivamente revisionato ed

ampliato grazie al lavoro di Aldo Pintus [5] e Alessio Rossotti [6]. Inizialmente l’applicazione era stata

pensata per modellare la parte View, l’estensione alla componente Model è stata introdotta solo a partire

dalla versione 3.0 sviluppata da Rossotti.

Con questo lavoro di tesi è stata realizzata la versione 5.0 di Protocode, che introduce la possibilità di

modellare, a fianco delle componenti Model e View, anche alcune parti della componente Controller. In

particolare, questo lavoro si concentra sull’utilizzo dei dati del Model da parte della View. Per ogni scena

l’utente di Protocode può selezionare i dati del model necessari al funzionamento della scena stessa. Tali

dati possono poi essere collegati (anche in modo bidirezionale) ai controlli che compongono la stessa scena.

Inoltre, a causa della deprecazione di molte librerie su cui la versione 4.0 si basava, è stato necessario

riprogettare completamente il codice per adattarlo a framework più moderni, in favore di una maggiore

manutenibilità ed estensibilità futura del progetto. Protocode 4.0 era basato sul framework EmberJS 1.6,

mentre Protocode 5.0 è basato su React 16.6.0.

Figura 4.1 Schermata principale di Protocode

50

Protocode è basato sul paradigma WYSIWYG (What You See Is What You Get), in modo da fornire all’utente

un feedback immediato e fedele su come apparirà il prodotto finito. Questo concetto è comune ad altri

prodotti di progettazione visuale, quali ad esempio Interface Builder di XCode o Layout Editor di Android

Studio, entrambi dedicati alla progettazione di interfacce utente per via grafica. La somiglianza consente a

sviluppatori provenienti da altri prodotti di trovare familiarità nell’interfaccia di Protocode, riducendo

quindi la learning curve di Protocode stesso.

Vi sono due sostanziali differenze tra Protocode e i suoi competitor: la tipologia di prodotto che viene

generato a seguito della fase di prototipizzazione e la possibilità di creare prototipi anche per quanto

riguarda le componenti Model e Controller.

Per quanto riguarda il prodotto che viene generato, Protocode consente di esportare i prototipi in un

formato strutturato, basato su XML, e non grafico. Questo permette poi di generare automaticamente il

codice per diverse piattaforme software (si veda la descrizione di MobileCodeGenerator nel capitolo

successivo).

Per quanto riguarda invece la prototipizzazione delle componenti Model e Controller, Protocode consente

in modo intuitivo e guidato di produrre sia un modello dei dati di interesse per l’applicazione, sia un

modello per la persistenza di tali dati, sia un meccanismo di connessione di tali dati con le interfacce

grafiche.

Al fine di implementare tali requisiti e per contenere la complessità di utilizzo, l’applicativo mette a

disposizione diversi editor, ciascuno con una precisa prerogativa

View Editor

Consente di progettare le viste dell’applicazione. E’ basato sul drag and drop di componenti quali,

ad esempio, immagini, pulsanti, caselle di testo e testi statici. Per ogni controllo creato è possibile

modificare attributi relativi alla posizione e alla spaziatura rispetto agli altri componenti. Inoltre, in

linea con le indicazioni dei principali produttori di sistemi operativi mobili, è possibile definire

vincoli di posizione e di dimensione tra i vari elementi, in modo da creare interfacce adattabili a

schermi di dimensioni diverse. Per ogni classe di controlli sono inoltre disponibili proprietà

specifiche (il placeholder di una casella di testo, o lo stile di un testo statico, ad esempio). Alcune di

queste proprietà specifiche possono essere collegate ai dati disponibili nel contesto della scena in

cui sono inseriti (si veda nel seguito il Scene Model Editor).

Model Editor

Consente di progettare il modello dei dati e il modello di persistenza dell’applicazione. E’ possibile

scegliere tra quattro tipologie di persistenza: shared preferences, file storage, relational database e

cloud database. Per ciascuna tipologia, è possibile definire la struttura dei dati che vengono salvati

utilizzando quel modello di persistenza. E’ quindi possibile definire entità e relazioni per il relational

database, le chiavi del dizionario delle shared preferences, i files accessibili nel file storage e la

struttura degli oggetti archiviati nel cloud database (la cui implementazione si basa su Firebase

Realtime Database)

Scene Model Editor

Consente di definire, per ogni scena, quali dati estrarre dai diversi modelli di persistenza perché

siano resi disponibili ai controlli all’interno della scena. Per ogni dato estratto è necessario

scegliere un nome identificativo. Questo consente all’utente, attraverso il view editor, di collegare

tali dati a specifiche proprietà dei controlli, astraendo dalla complessità della persistenza.

51

Manifest Editor

Consente di definire proprietà dell’applicazione nel suo complesso quali, ad esempio, il nome

dell’applicazione o dell’ente sviluppatore.

Di tali editor, il view editor e il model editor erano già disponibili nelle versioni precedenti. Il model editor

non ha subito modifiche sostanziali da un punto di vista concettuale durante questo lavoro di tesi, mentre

al view editor è stata aggiunta la possibilità di connettere proprietà dei controlli a proprietà di oggetti

estratti dal Model. Il Scene Model Editor è interamente frutto di questo lavoro di tesi. Il Manifest Editor era

una parte del Model Editor nella versione precedente, ma data la diversa natura concettuale dei dati da

esso gestiti è stato estratto come componente di primo livello.

Figura 4.2 Schermata dedicata alle connessioni tra modello e vista

Figura 4.3 Esempio di output di Protocode

52

4.2 Software e framework utilizzati Protocode 5.0 è una applicazione web basata su ReactJS ed è accessibile tramite tutti i browser che

supportano HTML5 e ECMAscript 6. Oltre a Javascript (nella sua estensione JSX) ed HTML5, durante lo

sviluppo sono stati utilizzati Yarn come gestore di pacchetti, Sass per la generazione di fogli di stile, e altre

librerie dell’ecosistema di ReactJS che verranno descritte nel seguito.

4.2.1 ReactJS

ReactJS [26] è il framework di base su cui si innesta tutta l’architettura di Protocode. E’ un framework

recente, rilasciato da Facebook nel 2015, ma in forte crescita, con una community di sviluppatori molto

attiva e un ottimo supporto. Tra i framework web più popolari si affianca ad AngularJS, EmberJS e

Backbone.

ReactJS è fortemente basato sulla composizione di componenti, piuttosto che sull’ereditarietà, e questo

consente un forte riuso del codice. A differenza di tutti gli altri framework, che sono basati su data binding

a due vie, ReactJS si basa sul concetto di esprimere la vista come funzione del modello. Grazie all’adozione

del VirtualDom, un albero di componenti parallelo e legato al classico albero DOM di cui costituisce una

estensione, i componenti sono organizzati gerarchicamente e i dati vengono passati solo da un componente

ai suoi figli, creando così un flusso direzionale dalla radice alle foglie. Non vi è flusso di dati nella direzione

opposta, ma solo di eventi. I componenti di ReactJS possono essere sia stateful che stateless. Ogni

componente possiede un metodo render nel quale viene definito il corrispondente sottoalbero (l’albero

avente radice in esso) di componenti (sia componenti definiti utilizzando ReactJS sia componenti nativi del

DOM. Il metodo render ha accesso alle proprietà ricevute dal componente padre e all’eventuale stato

interno del componente stesso e può quindi personalizzare il sottoalbero conseguentemente. L’espansione

dei componenti ReactJS procede ricorsivamente fino ad ottenere un albero costituito da soli elementi DOM

nativi, che può quindi essere mostrato dal browser.

La scelta di ReactJS come framework nasce dai seguenti fattori:

Attività della community online

Semplicità del framework (i concetti alla base di ReactJS sono molti meno di quelli alla base di altri

framework)

Avanzamento di ReactNative come piattaforma per lo sviluppo di applicazioni mobili

Ricchezza dell’ecosistema di plugin

Approccio innovativo al flusso dei dati

4.2.2 Yarn

Yarn [27] è un gestore di pacchetti basato su NodeJS. Nel mercato, si presenta come una evoluzione di

NPM, e si affianca ad altri gestori di pacchetti quali, ad esempio, NPM stesso, Bower (deprecato dagli stessi

sviluppatori, ma ancora in uso) e PNPM.

La scelta di Yarn come gestore di pacchetti si basa sulla maggiore velocità rispetto ad NPM e alla maggiore

popolarità rispetto a PNPM, che corrisponde quindi ad un più ampio supporto nella community. Per gli

elementi di interesse del progetto, da un punto di vista funzionale, i tre prodotti sono equivalenti.

Yarn è stato utilizzato per installare React e tutte le sue dipendenze, oltre che i vari plugin descritti nel

seguito.

53

4.2.3 Redux e Redux-persist

Redux [28] è una libreria per la gestione dello stato che si presenta come un “predictable state container”.

Infatti, l’idea centrale di Redux è proprio quella di schematizzare l’evoluzione dello stato come la risposta

ad una azione. Quando un’azione viene generata, lo stato successivo è calcolato da una funzione pura,

detta reducer, che prende come argomenti soltanto lo stato precedente e l’azione stessa. Questo rende

molto semplice tracciare l’evoluzione dello stato, replicare errori ed evitare problemi comuni di gestione

dello stato, che sono tradizionalmente molto presenti in un linguaggio come JavaScript.

Redux non nasce come libreria associata a React, ma si integra perfettamente con il concetto alla base di

React che la vista debba essere una funzione dello stato. Infatti, Redux mantiene lo stato dell’applicazione

fino a quando un componente non genera una azione. A quel punto lo stato successivo viene calcolato dal

reducer e il VirtualDom, essendo globalmente una funzione dello stato, può essere aggiornato di

conseguenza. La connessione tra Redux e React è implementata da un apposito plugin: react-redux. Questo

plugin si occupa di generare automaticamente componenti ReactJS connessi con lo stato mantenuto da

Redux attraverso due funzioni configurabili dall’utente e dedicate, rispettivamente, ad estrarre il

sottoinsieme dello stato di interesse per l’albero DOM radicato nel componente connesso stesso e a

lanciare azioni sullo stato.

Nell’ecosistema di Redux, di particolare interesse per Protocode è il plugin redux-persist, che si occupa di

mantenere lo stato nel database del browser (Local Storage) in modo completamente automatico. Lo stato

viene persistito dopo ogni azione, e ripristinato ad ogni riavvio dell’applicazione.

Un altro plugin di interesse per Protocode è redux-logger, che consente di registrare ogni azione inviata al

reducer nella console del browser, ottenendo così una traccia riproducibile.

4.2.4 Bootstrap

Bootstrap [29] è uno tra i più popolari framework HTML e CSS disponibili sul mercato. Il principale focus del

progetto è fornire strumenti per descrivere interfacce utente adattabili a qualsiasi tipo di schermo. Inoltre,

Bootstrap mette a disposizione componenti HTML di uso comune, tra cui cards, barre di navigazione, layout

a schede e molto altro.

Sul mercato si affianca ad altri framework quali PureCSS, Foundation, Skeleton e molti altri. La scelta di

Bootstrap deriva dalla sua indiscussa popolarità, che si traduce poi in una community di sviluppatori attiva e

in grado di fornire un ottimo supporto, e dalla sua completezza e semplicità di uso.

In Protocode la parte puramente CSS di Bootstrap viene utilizzata direttamente, mentre per quanto

riguarda i vari componenti che richiedono un supporto JavaScript è stata utilizzato il plugin React-Bootstrap

che fornisce un’implementazione dichiarativa compatibile con React di tali componenti.

4.2.5 Altre librerie

ReactRouter [30] viene utilizzato per implementare, in modo dichiarativo, la navigazione all’interno

dell’applicazione.

FontAwesome [31] consente di utilizzare una vasta libreria di icone in modo semplice e diretto. Il

vantaggio di utilizzare icone largamente diffuse e conosciute sul web è nella loro immediata

riconoscibilità da parte di un utente

FileSaver [32] viene utilizzata per implementare in modo indipendente dal browser il download del

file contenente il modello strutturato dell’applicazione progettata utilizzando Protocode.

54

XmlBeautifier [33] viene utilizzato per migliorare l’aspetto grafico del file XML e renderlo più

leggibile da un utente.

Uniqid [34] viene utilizzata per generare un id univoco per ogni dato di interesse per l’applicazione.

Babel [35] viene utilizzata per convertire il codice JSX in JavaScript compatibile con la maggior parte

dei browser

55

4.3 Architettura dell’applicazione In questo capitolo viene descritta l’architettura dell’applicazione Protocode 5.0 nei diversi aspetti di

Architettura software

Design dei dati

Connessione tra dati e vista

Design della navigazione

Design dell’interfaccia utente

4.3.1 Architettura software

In questa sezione viene presentata l’architettura software dell’applicazione Protocode. L’architettura

dell’applicazione è fortemente basata sull’integrazione tra Redux e React. Come mostrato nel seguente

diagramma, è chiaramente possibile suddividere l’applicazione in tre livelli logici: i dati (indicati nel riquadro

blu), i componenti di accesso ai dati (riquadro rosso) e i componenti di visualizzazione dei dati e interazione

con l’utente (riquadro verde).

Figura 4.4 Architettura di alto livello di Protocode

56

Ciascuno di questi livelli è gestito da una libreria apposita: Redux per i dati, React per l’interazione con

l’utente e la visualizzazione dei dati, e React-Redux per i componenti di connessione.

L’architettura comprende anche i componenti dedicati alla modellazione di applicazioni per smartwatch,

che non verranno descritti in questo documento in quanto non rilevanti ai fini della tesi.

Gestione dei dati: Redux

In Protocode lo stato dell’applicazione è interamente gestito da Redux. Questa sezione presenta le

principali scelte architetturali inerenti la definizione della forma dello stato e delle regole per il suo

aggiornamento, implementate attraverso i reducers. La forma dello stato e le azioni supportate dai reducers

sono definiti nel paragrafo §4.3.2 Design dei dati.

Lo stato dell’applicazione, come supportato da Redux, consiste di un singolo oggetto JavaScript. Al suo

interno, ogni chiave corrisponde ad una precisa classe di oggetti. La relazione di composizione tra oggetti di

classi diverse viene eliminata e sostituita da riferimenti basati su identificativi univoci, inspirandosi al

concetto di chiave esterna tipico dei database relazionali. Questo consente di ottenere un oggetto con un

basso livello di nesting, più maneggevole per il reducer.

Ogni chiave dell’oggetto che rappresenta lo stato non contiene semplicemente una lista di tutti gli oggetti

appartenenti alla classe di riferimento, ma una struttura dati che ne faciliti il riferimento, che chiameremo

DataStore. Un DataStore memorizza gli oggetti in base ad un campo che funge da chiave primaria per il loro

riferimento, ma mantiene anche una serie di indici, configurabili a seconda del caso, per facilitare

operazioni di query. L’unico vincolo implementativo è che tutti i campi indicizzati siano immutabili. Ciò

tuttavia non costituisce una limitazione sensibile nel caso di Protocode, dato che tipicamente i campi

indicizzati di ogni oggetto contengono quei riferimenti introdotti per semplificare le relazioni di

composizione, e quindi sono immutabili per loro stessa natura. Questo rende estremamente efficienti le

operazioni di ricerca di tutti i figli di un certo elemento, ad esempio tutti i vincoli associati ad un controllo o

tutti i controlli di una vista applicativa.

In modo analogo, anche il reducer è definito attraverso la composizione di più funzioni, una per ciascuna

chiave dello stato. Ciascuna di queste funzioni, chiamata con un abuso di linguaggio reducer nel linguaggio

di Redux, riceve quindi solo il corrispondente segmento dello stato complessivo, e produce in output per

ogni azione la versione prossima del segmento stesso. I segmenti sono poi automaticamente riaggregati da

Redux a formare lo stato prossimo. Non tutte le azioni sono di interesse per tutti i segmenti dello stato, e in

questo caso i reducers ignorano le azioni non rilevanti per i dati che gestiscono ritornando, come stato

prossimo, lo stato precedente stesso.

Accesso ai dati: React-redux

I componenti di accesso ai dati si occupano di colmare il divario tra lo stato amministrato da Redux e la vista

implementata con React. La libreria utilizzata per la loro creazione è react-redux, che consente la

generazione automatica delle connessioni tra questi componenti e lo stato.

La progettazione di tali componenti si basa su tre elementi: la definizione di un componente React

“tradizionale” e la definizione di due funzioni chiamate mapStateToProps e mapDispatchToProps. Queste

due funzioni si occupano entrambe di creare proprietà che saranno poi iniettate nel componente come se

gli fossero passate dal componente padre nel VirtualDom. mapStateToProps riceve come parametro lo

stato corrente, e ne seleziona alcuni elementi, mentre mapDispatchToProps riceve come parametro la

funzione dispatch, che consente di inviare azioni allo stato, e ne crea dei wrapper semanticamente

57

significativi per il componente: è possibile creare un wrapper senza parametri che chiami dispatch con

parametri predefiniti, o semplicemente iniettare la funzione nel componente con un nome più significativo

di dispatch. Tipicamente viene creato un wrapper per ogni tipologia di azione da inviare allo stato. Grazie a

queste proprietà i componenti di connessione tra stato e vista possono accedere ai dati e inviare azioni, e

possono passare, sempre per mezzo di proprietà, dati e funzioni di emissione di azioni ai propri figli, agendo

così da punto di connessione per un intero sottoalbero.

La definizione delle funzioni mapStateToProps dei vari componenti di connessione con lo stato in Protocode

è supportata da un insieme di funzioni dette selettori. È definito un selettore per ogni classe di componenti.

Il ruolo dei selettori, oltre a quello di astrarre dalle modalità di estrazione dai DataStore che compongono lo

stato corrente, è quello di denormalizzare gli oggetti stessi sostituendo i riferimenti tramite identificatori

con l’oggetto referenziato, e quindi ricostruendo le relazioni, soprattutto di composizione. Ogni selettore

quindi sfrutta selettori di altre classi al fine di produrre un oggetto denormalizzato e più facilmente

utilizzabile dalla view.

Visualizzazione dei dati e interazione con l’utente: React

I componenti di visualizzazione dei dati ed interazione con l’utente sono definiti a partire dal framework

ReactJS.

Ciascuno degli editor precedentemente descritti è caratterizzato da un set di componenti, che ne vanno a

definire l’aspetto grafico e la logica di funzionamento. Nei seguenti paragrafi verranno descritti, per ogni

editor, i componenti principali.

Model Editor

Il model editor è composto da un componente padre che permette la navigazione tra le diverse modalità di

archiviazione dei dati, e da quattro componenti figli, uno per ogni modalità di archiviazione:

SharedPreferences permette la definizione di ciascun record da archiviare tramite

sharedPreferences. Per ogni record, è possibile definire chiave, tipo e valore di default.

File Storage permette la definizione dei files di interesse per l’applicazione, che dovranno essere

gestiti e archiviati nel file system del dispositivo mobile. Per ogni file, è possibile definire nome,

estensione, e marcare se sia un file temporaneo.

Relational database permette la definizione delle entità che verranno archiviate nel database. Per

ogni entità è poi disponibile un apposito editor dove è possibile configurare il nome della chiave

primaria, eventuali attributi (definiti da nome e tipo) e relazioni (definite da nome, cardinalità e

entità correlata)

Cloud database permette la definizione degli oggetti che saranno archiviati nell’albero JSON di

Firebase Realtime Database. E’ sempre disponibile l’oggetto RootObject, che mappa la radice

dell’albero DOM e può essere acceduto direttamente. Ogni altro oggetto può essere acceduto

attraverso la specifica del suo percorso a partire dal RootObject.

Ciascun editor permette inoltre di abilitare o disabilitare una specifica modalità di storage per l’applicazione

che si sta sviluppando, in modo da eliminare modalità che siano non di interesse e ridurre la dimensione

della codebase generata.

58

Smartphone & Tablet Editor

Lo smartphone & tablet editor consente di definire le varie schermate di un’applicazione mobile. Questo

componente è molto complesso, data dalla ricchezza di controlli e di opzioni per il layout, e delle dinamiche

di costituzione delle scene come composizione di viste.

Come definito nel modello di riferimento (§3.1.1), la gerarchia di elementi che costituiscono la view è la

seguente:

Scena: corrisponde al concetto di schermata di un’applicazione. È l’elemento di primo livello nella

gerarchia, non è contenuto in nessun altro elemento.

View controller: corrisponde al concetto di vista. Le viste vengono aggregate a formare scene. Ogni

scena può contenere una sola vista, più viste accessibili tramite un menù a schede, o più viste

disposte sullo schermo simultaneamente in modo configurabile dall’utente.

Control chain: è un elemento opzionale nella catena, come descritto al punto successivo. Le control

chains implementano meccanismi di distribuzione di controlli sullo schermo lungo un dato asse.

Ui Controls: ogni elemento rappresenta un controllo, sia esso un testo, un pulsante o un video

player. I controlli possono essere inseriti, per motivi di posizionamento, in una control chain,

oppure possono essere inseriti direttamente come figli di un view controller.

I componenti fondamentali per questo editor sono:

Navigator permette di selezionare quale vista o scena modificare

Canvas mostra la vista o la scena corrente all’interno di una cornice che rappresenta uno

smartphone o un tablet. Tipologia di device, produttore e modello sono selezionabili tramite un

apposito selettore. L’immagine mostrata è dotata della funzione di zoom.

Palette offre una selezione di controlli che possono essere aggiunti alla vista corrente tramite drag

and drop.

Inspector permette di modificare le proprietà di ogni singolo componente inserito tramite la

palette. In base all’elemento selezionato sulla canvas, l’inspector modifica il suo aspetto e le opzioni

disponibili. È sempre possibile modificare il nome di un componente, i suoi vincoli di posizione, le

dimensioni e le spaziature (padding e margine). Altre proprietà sono disponibili in base al

componente: per le caselle di testo è disponibile il testo contenuto, per i pulsanti è disponibile un

menù che consente di selezionare a quale vista o a quale scena passare quando il tasto viene

premuto. Lo stesso componente viene anche utilizzato per la modifica del menù dell’applicazione,

anche se questo non è un controllo nel senso proprio del termine.

Scene Model Editor

Il scene model editor permette di selezionare dati provenienti dai vari meccanismi di persistenza perché

siano resi disponibili ai controlli inseriti nella view. Chiameremo questi dati “elementi connessi”. Questo

editor si compone di un selettore che consente di selezionare di volta in volta su quale scena andare a

lavorare, e di un componente che, ricevuta in ingresso la scena, mostra gli elementi attualmente connessi e

gli elementi disponibili per una connessione.

Manifest Editor

Il manifest editor è un componente molto semplice, costituito da un solo form, che consente la modifica

delle proprietà globali dell’applicazione che si sta progettando, che nella versione attuale sono soltanto

due: il nome dell’applicazione e il prefisso che identifica lo sviluppatore.

59

4.3.2 Design dei dati

Questa sezione presenta il modello dei dati utilizzato da Protocode e gestito tramite Redux. Dato che

l’obiettivo finale di Protocode è quello di generare un modello dell’applicazione compatibile con le

definizioni di cui al capitolo §3, lo stato mantenuto in Protocode deve contenere tutte le informazioni

necessarie allo scopo. Conseguentemente, ciascuna classe di oggetti definita dal modello di riferimento

corrisponde ad una o più classi definite nel modello dei dati di Protocode, in modo che le istanze delle

seconde possano essere utilizzate per definire istanze delle prime. È possibile quindi pensare che il modello

dei dati utilizzato da Protocode possa essere ottenuto applicando una serie di trasformazioni al modello di

riferimento.

La trasformazione più significativa riguarda le relazioni, soprattutto di composizione. Ogni relazione infatti

viene espressa inserendo in ciascuno degli oggetti coinvolti riferimenti non equivoci alle altre parti

coinvolte. Questo tuttavia non è sufficiente per gestire le relazioni di composizione: quando infatti un

elemento viene eliminato, anche tutti gli elementi in esso contenuti devono essere eliminati

conseguentemente, e questo vale ricorsivamente. Al fine di implementare in modo efficiente queste

eliminazioni, è necessario che ogni oggetto contenuto possieda riferimenti a tutti gli oggetti che lo

contengono, ricorsivamente. Se quindi C è contenuto in B e B è contenuto in A, C dovrà possedere il

riferimento sia a B che ad A. Questo semplifica la gestione delle operazioni di cancellazione: il reducer che si

occupa di gestire le entità di tipo C può reagire alle azioni di cancellazione di A e di B in modo opportuno,

poiché possiede tutti gli elementi per determinare su quali oggetti da lui gestiti tali azioni producano un

effetto. Questa trasformazione, negli ambienti che usano Redux, è nota come normalizzazione.

A questo punto è possibile definire lo stato di Protocode come un’oggetto JavaScript che contiene, per ogni

chiave, un DataStore dedicato ad una (o più) classi di oggetti, ciascuna delle quali può essere collegata al

modello di riferimento. Lo stato di Protocode ha la seguente forma:

{

application: applicationObject,

scenes: DataStore<Scene>,

viewControllers: DataStore<ViewController>,

alertDialogs: DataStore<AlertDialog>,

progressDialogs: DataStore<ProgressDialog>,

asyncTasks: DataStore<AsyncTask>,

uiPhoneControls: DataStore<UiPhoneControl>,

constraints: DataStore<Constraint>,

listViewCells: DataStore<ListViewCell>,

gridViewCells: DataStore<GridViewCell>,

sourceTypes: DataStore<SourceType>,

controlChains: DataStore<ControlChain>,

containers: DataStore<Container>,

navigations: DataStore<Navigation>,

menu: DataStore<MenuItem>,

dataHandlers: data handlers object,

preferenceRecords: DataStore<PreferenceRecord>,

fileStorageRecords: DataStore<StorageRecord>,

entities: DataStore<Entity>,

entityAttributes: DataStore<EntityAttribute>,

entityRelations: DataStore<EntityRelation>,

cloudObjects: DataStore<CloudObject>,

cloudObjectAttributes: DataStore<CloudObjectAttribute>,

adapterBindings: DataStore<AdapterBinding>,

modelConnectors: DataStore<ModelConnector>

}

Questo schema mostra le chiavi che compongono lo stato di Protocode e il contenuto di ciascuna di esse.

Fatta eccezione per due chiavi, tutte le chiavi contengono un DataStore (descritto in Gestione dei dati:

60

Redux) dedicato ad una o più classi di oggetti del modello di riferimento. In generale ogni classe del

modello ha una propria controparte in Protocode, e tale controparte possiede un proprio DataStore.

L’eccezione più notevole a questa politica riguarda i controlli utente, che sono tutti memorizzati in un unico

DataStore dedicato ad UiPhoneControl (si può vedere questa operazione come una riduzione alla

sovraclasse, tipica della progettazione dei database).

Nei prossimi paragrafi verranno presentate le varie chiavi (suddivise per area di riferimento: Model, View o

Controller), la struttura degli oggetti in esse memorizzati e come questi corrispondono alle definizioni del

modello di riferimento, e le azioni a cui reagiscono i rispettivi reducers. Ogni DataStore infatti evolve in base

ad azioni diverse, che vengono interpretate dai reducer e convertite in opportuni aggiornamenti dello stato.

Design dei dati riguardanti il model

dataHandlers

Questa chiave costituisce un caso eccezionale rispetto alle altre chiavi in quanto non contiene un DataStore

ma un singolo semplice oggetto JavaScript che possiede le seguenti proprietà

Proprietà Descrizione

preferences Contiene lo stato di attivazione della persistenza tramite SharedPreferences

files Contiene lo stato di attivazione della persistenza tramite FileStorage

database Contiene lo stato di attivazione della persistenza tramite database relazionale

cloud Contiene lo stato di attivazione della persistenza tramite cloud Tabella 4.1 Proprietà di "data handler object"

Questo oggetto contiene quindi l’indicazione di quali strategie di persistenza il modellatore vuole includere

nell’applicazione. Questo oggetto non ha una controparte nel modello di riferimento, ma contiene

informazioni necessarie alla sua generazione: il modello esportato conterrà informazioni relative alle sole

modalità di persistenza il cui stato di attivazione (contenuto in questo oggetto) è vero.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

ENABLE_PREFERENCES: abilita la persistenza tramite SharedPreferences

DISABLE_PREFERENCES: disabilita la persistenza tramite SharedPreferences

ENABLE_FILE_STORAGE: abilita la persistenza tramite files

DISABLE_FILE_STORAGE: disabilita la persistenza tramite files

ENABLE_DATABASE: abilita la persistenza tramite database relazionale

DISABLE_DATABASE: disabilita la persistenza tramite database relazionale

ENABLE_CLOUD_DATABASE: abilita la persistenza tramite database cloud

DISABLE_CLOUD_DATABASE: disabilita la persistenza tramite database cloud

preferenceRecords

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe PreferenceRecord del modello di riferimento (§3.2.3). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

61

Proprietà Descrizione Indicizzata

id Identificativo univoco si

key La chiave associata alla preferenza all’interno del dizionario delle sharedPreferences

type Il tipo del valore associato alla chiave. Può essere uno tra string, int, long, boolean, float e double

value Il valore di default associato alla chiave specificata Tabella 4.2 Proprietà di oggetti di tipo PreferenceRecord

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della

proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel

modello.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_PREFERENCE_RECORD: crea un oggetto in questo DataStore, inizializzando tutte le

proprietà con valori contenuti nell’azione

DELETE_PREFERENCE_RECORD: cancella un oggetto da questo DataStore, dato il suo id

DISABLE_PREFERENCES: cancella tutti gli oggetti da questo DataStore

fileStorageRecords

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe StorageRecord del modello di riferimento (§3.2.2). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Il nome del file

extension L’estensione del file. Può essere testo (txt), immagine (png) o altro

isTemp Indica se il file sia temporaneo. Questo influenza la gestione del file da parte del sistema operativo: i files temporanei possono essere eliminati in caso di necessità di recuperare spazio su disco

Tabella 4.3 Proprietà di oggetti di tipo FileStorageRecord

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della

proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel

modello.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_FILE_STORAGE_RECORD: crea un oggetto in questo DataStore, inizializzando tutte le

proprietà con valori contenuti nell’azione

DELETE_FILE_STORAGE_RECORD: cancella un oggetto da questo DataStore, dato il suo id

DISABLE_FILE_STORAGE: cancella tutti gli oggetti da questo DataStore

entities

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe Entity del modello di riferimento (§3.2.1). Gli oggetti contenuti in questo DataStore possiedono le

seguenti proprietà

62

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Il nome dell’entità. Deve essere univoco all’interno del database.

primaryKey Il nome del campo che svolge il ruolo di chiave primaria. E’ implicitamente di tipo string.

Tabella 4.4 Proprietà di oggetti di tipo Entity

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della

proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel

modello. Le relazioni entityAttributes e entityRelations sono modellate mediante attributi associati alle

entità EntityAttribute e EntityRelation contenute in altre chiavi dello stato di Protocode.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_ENTITY: crea un oggetto in questo DataStore, inizializzando tutte le proprietà con valori

contenuti nell’azione

EDIT_ENTITY: modifica un oggetto di questo DataStore, dato il suo id. Le modifiche sono specificate

nell’azione

DELETE_ENTITY: cancella un oggetto da questo DataStore, dato il suo id

DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore

entityAttributes

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe EntityAttribute del modello di riferimento (§3.2.1). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

entityId Identificativo dell’entità relazionale (§3.2.4) che possiede l’attributo si

name Il nome dell’attributo

type Il tipo dell’attributo: string, int, date, boolean, float oppure double Tabella 4.5 Proprietà di oggetti di tipo EntityAttribute

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della

proprietà id, necessaria in Protocode per esprimere relazioni e omessa poi in fase di generazione del

modello, e della proprietà entityId necessaria per normalizzare la relazione entityAttributes di Entity

(§3.2.1).

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_ENTITY_ATTRIBUTE: crea un oggetto in questo DataStore, inizializzando tutte le proprietà

con valori contenuti nell’azione

DELETE_ENTITY_ATTRIBUTE: elimina un oggetto da questo DataStore, dato il suo id.

DELETE_ENTITY: cancella tutti gli oggetti in relazione con l’oggetto Entity eliminato, il cui id è

contenuto nell’azione

DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore

63

entityRelations

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe EntityRelation del modello di riferimento (§3.2.1). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

fromEntityId Identificativo dell’entità relazionale (§3.2.4) che possiede l’attributo si

name Il nome della relazione

cardinality La cardinalità della relazione, a scelta tra 1:1, 1:N, N:1.

fromEntity L’entità sorgente della relazione

toEntity L’entità destinazione della relazione si Tabella 4.6 Proprietà di oggetti di tipo EntityRelation

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della

proprietà id, necessaria in Protocode per esprimere relazioni e omessa poi in fase di generazione del

modello, e della proprietà fromEntityId necessaria per normalizzare la relazione entityRelations di Entity

(§3.2.1).

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_ENTITY_RELATION: crea un oggetto in questo DataStore, inizializzando tutte le proprietà

con valori contenuti nell’azione

DELETE_ENTITY_RELATION: elimina un oggetto da questo DataStore, dato il suo id.

DELETE_ENTITY: cancella tutti gli oggetti in relazione con l’oggetto Entity eliminato, il cui id è

contenuto nell’azione

DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore

cloudObjects

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe CloudObject del modello di riferimento (§3.2.4). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Il nome di questa classe di oggetti. Deve essere univoco nel contesto del database cloud

Tabella 4.7 Proprietà di oggetti di tipo CloudObject

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della

proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel

modello. La relazione attributes è modellata mediante un attributo associato all’entità

CloudObjectAttribute, le cui istanze sono contenute in altre chiavi dello stato di Protocode.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_CLOUD_OBJECT: crea un oggetto in questo DataStore, inizializzando tutte le proprietà con

valori contenuti nell’azione

EDIT_CLOUD_OBJECT: modifica un oggetto di questo DataStore, dato il suo id. Le modifiche sono

specificate nell’azione

64

DELETE_CLOUD_OBJECT: cancella un oggetto da questo DataStore, dato il suo id

DISABLE_CLOUD_DATABASE: cancella tutti gli oggetti da questo DataStore

ENABLE_CLOUD_DATABASE: crea l’oggetto radice dell’albero in questo DataStore con l’id fisso (il

cui valore è “__root__”) e nome fisso (il cui valore è “Root Object”)

cloudObjectAttributes

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe EntityAttribute del modello di riferimento (§3.2.4). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

objectId Identificativo dell’oggetto cloud (si veda Archiviazione tramite database cloud) che possiede l’attributo

si

name Il nome dell’attributo

type Il tipo dell’attributo, a scelta tra string, int, float, double, boolean, ref, ref_list. I tipi ref e ref_list indicano rispettivamente che questo attributo contiene un oggetto e una lista omogenea di oggetti

object Valido solamente per attributi di tipo ref e ref_list, contiene l’identificativo dell’oggetto cloud (si veda Archiviazione tramite database cloud) che descrive la tipologia di oggetti memorizzati in questo attributo

si

Tabella 4.8 Proprietà di oggetti di tipo CloudObjectAttribute

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della

proprietà id, necessaria in Protocode per esprimere relazioni e omessa poi in fase di generazione del

modello, e della proprietà objectId necessaria per normalizzare la relazione attributes di CloudObject

(§3.2.4).

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_CLOUD_OBJECT_ATTRIBUTE: crea un oggetto in questo DataStore, inizializzando tutte le

proprietà con valori contenuti nell’azione

DELETE_ CLOUD_OBJECT _ATTRIBUTE: elimina un oggetto da questo DataStore, dato il suo id.

DELETE_ CLOUD_OBJECT: cancella tutti gli oggetti in relazione con l’oggetto CloudObject eliminato,

il cui id è contenuto nell’azione

DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore

65

Design dei dati riguardanti la view

application

Questa chiave costituisce un caso eccezionale rispetto alle altre chiavi in quanto non contiene un DataStore

ma un singolo semplice oggetto JavaScript che possiede le seguenti proprietà

Proprietà Descrizione

name Il nome dell’applicazione

companyIdentifier L’identificativo dello sviluppatore, segue le convenzioni dei packages Java Tabella 4.9 Proprietà dell'oggetto Application

Le relazioni scenes, viewControllers e dataHandler non sono esplicitamente modellate, in quando la

relazione con dataHandler è banale data l’unicità del dataHandler stesso (come descritto in dataHandlers),

e le relazioni scenes e viewControllers contengono tutte le scene e tutti i viewController definiti, e possono

essere quindi banalmente dedotte dai DataStore dedicati a tali classi di dati.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_APPLICATION: inizializza name a “newApp” e companyIdentifier a “it.polimi”

CHANGE_NAME: modifica la proprietà name impostando un valore contenuto nell’azione

CHANGE_COMPANY_IDENTIFIER: modifica la proprietà companyIdentifier impostando un valore

contenuto nell’azione

La mancanza dell’azione DELETE_APPLICATION è intenzionale: tale azione ha effetti sull’intero stato, e viene

gestita separatamente eseguendo un hard set dell’intero stato ad un valore predefinito in attesa della

creazione di una nuova applicazione. Se così non fosse, ogni segmento dello stato dovrebbe contenere

un’azione di reset del suo contenuto associata ad azioni di questa tipologia. Tale soluzione sarebbe tuttavia

troppo pesante da implementare.

scenes

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe Scene del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le seguenti

proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Il nome della scena

launcher Indica se la scena viene visualizzata all’avvio dell’applicazione. Solo una scena può essere contrassegnata come launcher

Tabella 4.10 Proprietà di oggetti di tipo Scene

Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento. Le proprietà

typeSmartphone, typeTablet e le relazioni childViewControllers e parentViewControllers sono dedotte a

partire da informazioni memorizzate in altre chiavi. In particolare le relazioni con altri viewController sono

memorizzate utilizzando attributi dei viewController stessi, e le proprietà typeSmartphone e typeTablet

sono rispettivamente allocate ai parentViewControllers corrispondenti.

66

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_SCENE: crea un oggetto in questo DataStore, inizializzando tutte le proprietà con valori

contenuti nell’azione

DELETE_ SCENE: elimina un oggetto da questo DataStore, dato il suo id.

EDIT_SCENE: modifica un oggetto in questo DataStore dato il suo id e le modifiche da applicare.

Qualora le modifiche coinvolgano l’attributo launcher, viene assicurato che solo l’ultima scena che

abbia richiesto di essere launcher abbia tale attributo impostato a vero.

viewControllers

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe ViewController del modello di riferimento. Differentemente dal modello di riferimento, tuttavia, i

viewController che svolgono il ruolo di parentViewController contengono anche la definizione del tipo di

layout che essi utilizzano (SINGLE_VC, SINGLE_VC_TAB, MULTI_VC). Gli oggetti contenuti in questo

DataStore possiedono le seguenti proprietà

ViewController Descrive un frammento utilizzato per comporre una scena Indicizzata

id Identificativo univoco si

name Il nome del viewController.

backgroundColor Il colore di sfondo del viewController, in formato RGB esadecimale

backgroundImage Il path dell’immagine di sfondo del viewController. Ha priorità sul colore.

type Valido solo per i parentViewControllers. Rappresenta il tipo di layout della scena, può essere SingleVC, SingleVCTab oppure MultiVC.

scene Valido solo per i parentViewControllers. Indica il riferimento alla scena di cui questo viewController definisce il layout

si

Tabella 4.11 Proprietà di oggetti di tipo ViewController

Gli attributi id, name, backgroundColor e backgroundImage ricalcano gli omonimi del modello di

riferimento. Gli attributi scene e type sono stati introdotti per semplificare la gestione dei

parentViewControllers. Differentemente dal modello di riferimento, nel modello dei dati di Protocode ogni

scena possiede sempre due parentViewController, uno dedicato al layout per cellulare e l’altro al layout per

tablet. È quindi naturale associare la definizione del tipo di layout al relativo parentViewController. Questa

struttura può comunque essere ricondotta al modello di riferimento, esportando solamente quei

parentViewController il cui tipo sia MULTI_VC. Tutte le relazioni di contenimento in cui la classe

ViewController è coinvolta nel modello di riferimento con ruolo di contenitore sono implementate in

Protocode attraverso attributi delle entità figlie che contengono l’id del viewController contenitore.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_SCENE: crea due oggetti in questo DataStore, che costituiscono i due parentViewController

della scena creata con questa azione. I campi vengono inizializzati nel seguente modo

o backgroundColor e backgroundImage sono inizializzati ai valori di default

o scene viene impostato all’id della scena appena creata (contenuto nell’azione)

o type assume il valore predefinito SINGLE_VC

o id viene ottenuto concatenando l’id della scena con le parole chiave SMARTPHONE o

TABLET, a seconda del tipo di device su cui il viewController agisca da parentViewController

o name assume il valore predefinito “Parent ViewController smartphone” o

“parentViewController tablet” con le stesse regole del campo id

67

DELETE_ SCENE: elimina i parentViewController relativi alla scena eliminata, il cui id è contenuto

nell’azione stessa e nel campo scene degli elementi da eliminare

CREATE_VIEWCONTROLLER: crea un nuovo viewController (mai un parentViewController). I campi

sono inizializzati nel seguente modo

o id viene ottenuto dall’azione

o name assume il valore “ViewController{N}” dove {N} viene sostituito da un numero

progressivo

o backgroundColor viene inizializzato a “#ffffff” (bianco)

o backgroundImage viene inizializzato alla stringa vuota (nessuna immagine)

o scene assume il valore NULL

o type non è rilevante, assume convenzionalmente il valore SINGLE_VC

EDIT_VIEW_CONTROLLER: modifica un elemento di questo DataStore dato il suo id e le modifiche

da applicare

DELETE_VIEW_CONTROLLER: elimina un elemento di questo DataStore, dato il suo id (contenuto

nell’azione)

controlChains

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe ControlChain del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le

seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

viewControllerId Identificativo dell’oggetto di tipo ViewController a cui questa catena appartiene logicamente

si

type Il tipo di catena, ovvero il modo in cui viene calcolato lo spazio tra i vari controlli. Può essere spread, spread_inside, packed o weighted.

axis L’asse lungo cui i controlli vengono distributi, può essere orizzontale o verticale

spacing Lo spazio minimo tra mantenere tra i vari controlli inseriti nella catena, espresso in punti. Lo spazio effettivamente mantenuto varia in base al tipo di catena

bias Valido solo per catene packed, definisce il rapporto tra la dimensione dello spazio lasciato prima dei componenti e lo spazio totale disponibile al netto della dimensione dei componenti lungo l’asse della catena.

members Collezione degli identificativi degli oggetti appartenenti alla catena Tabella 4.12 Proprietà di oggetti di tipo ControlChain

Ad eccezione della proprietà viewControllerId, utilizzata per definire a quale viewController la catena

appartiene, consentendo così di definire la relazione controlChains della classe ViewController del modello

di riferimento, tutte le altre proprietà possiedono un’omonima corrispondente nel modello di riferimento.

La proprietà nControls del modello di riferimento è calcolata al momento dell’esportazione del modello,

mentre la relazione controls è definita da attributi impostati sui controlli stessi.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_CONTROL_CHAIN: crea una nuova catena di controlli (vuota) in questo DataStore. La

catena così creata non è valida (in quanto deve contenere almeno due controlli utente), e a meno

di modifiche successive non viene esportata.

68

CREATE_UI_PHONE_CONTROL: nel caso in cui il controllo creato con questa azione sia marcato

come appartenente ad una catena di controlli, il suo id viene aggiunto alla collezione members di

tale catena

DELETE_UI_PHONE_CONTROL: nel caso in cui l’identificativo del controllo eliminato con questa

azione sia contenuto nella collezione members di qualche catena, viene rimosso

EDIT_CONTROL_CHAIN: modifica i parametri di un elemento di questo DataStore, dato il suo id e le

modifiche da apportare (entrambi contenuti nell’azione)

DELETE_CONTROL_CHAIN: rimuove un oggetto da questo DataStore, dato il suo id (contenuto

nell’azione)

DELETE_VIEW_CONTROLLER: elimina tutte le catene appartenenti al viewController eliminato, il cui

id è contenuto sia nell’azione che nell’attributo viewControllerId degli elementi da eliminare

Al momento della creazione, i campi di questo oggetto sono inizializzati come segue

id assume il valore specificato nell’azione

viewControllerId assume il valore specificato nell’azione

type viene inizializzato a spread

axis viene inizializzato a horizontal

spacing viene inizializzato a 0

bias viene inizializzato a 0.5

members viene inizializzata ad una collezione vuota

menu

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe MenuItem (descritta in Menu e MenuItem) del modello di riferimento. Gli oggetti contenuti in questo

DataStore possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Il nome dell’elemento, utilizzato dall’utente per distinguerlo da altri elementi

title Il testo visualizzato nel menu per questo elemento Tabella 4.13 Proprietà di oggetti di tipo MenuItem

Tutte le proprietà elencate corrispondono a proprietà del modello di riferimento. Questo DataStore inoltre

consente di esportare nel modello la classe Menu, che possiede solo la relazione menuItems, che a sua

volta contiene tutti gli elementi di menù definiti per l’applicazione, ovvero tutti gli elementi di questo

DataStore.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_MENU_ITEM: crea un nuovo elemento in questo DataStore, i cui campi sono inizializzati a

partire da valori contenuti nell’azione stessa

EDIT_MENU_ITEM: modifica un elemento di questo DataStore, dato il suo id e le modifiche da

applicare (entrambi contenuti nell’azione)

DELETE_MENU_ITEM: elimina un elemento da questo DataStore, dato il suo id (contenuto

nell’azione)

69

uiPhoneControl

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze di tutte

le classi che estendono UiPhoneControl nel modello di riferimento, ad eccezione della classe Container i cui

elementi sono gestiti separatamente. Tutti gli elementi possiedono quindi le proprietà definite su

UiPhoneControl e UiControl nel modello di riferimento, più alcune proprietà distintive in base alla loro

classe specifica, più alcune proprietà dimensionali di competenza della classe DimensionConstraint nel

modello di riferimento. Questo è possibile grazie all’ereditarietà prototipica del linguaggio JavaScript. Gli

oggetti contenuti in questo DataStore possiedono le seguenti proprietà comuni

Proprietà Descrizione Indicizzata

id Identificativo univoco si

viewControllerId Identificativo univoco del ViewController a cui questo elemento appartiene

si

uiPhoneControlType Tipologia del controllo (necessaria per capire a quale sottoclasse di UiPhoneControl afferisce)

si

controlChainId Eventuale identificativo della catena di controlli (ControlChain) in cui il controllo è inserito

si

name Il nome del controllo, utilizzato per distinguerlo da altri controlli

title Il testo contenuto nel controllo (valido solo nei controlli che contengono testo)

minWidth La larghezza minima del controllo, non può essere modificata dall’utente, viene utilizzata come limite inferiore per l’attributo width quando la larghezza viene impostata dall’utente in modo assoluto

minHeight Come per minWidth, ma per l’altezza

defaultWidth La larghezza predefinita del componente. Non è modificabile dall’utente, prende il posto della larghezza nel caso in cui essa non sia espressa né in modo assoluto, né in modo percentuale, né sia vincolata dall’aspect ratio

defaultHeight Come per defaultWidth, ma per l’altezza

marginTop Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato superiore

marginBottom Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato inferiore

marginRight Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato destro

marginLeft Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato sinistro

paddingTop Lo spazio da lasciare all’interno del componente tra il lato superiore e il contenuto del componente

paddingRight Lo spazio da lasciare all’interno del componente tra il lato destro e il contenuto del componente

paddingBottom Lo spazio da lasciare all’interno del componente tra il lato inferiore e il contenuto del componente

paddingLeft Lo spazio da lasciare all’interno del componente tra il lato sinistro e il contenuto del componente

width La larghezza del componente. Può essere interpretata come un valore assoluto, una percentuale (nel range [0, 100]), o essere ignorato, in base al valore di widthMode

70

Proprietà Descrizione Indicizzata

height L’altezza del componente. Può essere interpretata come un valore assoluto, una percentuale (nel range [0, 100]), o essere ignorato, in base al valore di heightMode

ratioWidth Il numeratore dell’aspect ratio espresso come numero razionale

ratioHeight Il denominatore dell’aspect ratio espresso come numero razionale

posX La distanza, in punti, dell’estremo sinistro del componente dal bordo sinistro del viewController. Valida solo in assenza di vincoli lungo l’asse orizzontale

posY La distanza, in punti, del lato superiore del componente dal bordo superiore del viewController. Valida solo in assenza di vincoli lungo l’asse verticale

widthMode La modalità di espressione della larghezza. Può essere

exact: width contiene la larghezza in dip del componente

percent: width contiene la larghezza percentuale [0, 100] del componente rispetto alla larghezza del viewController

auto: la larghezza è calcolata automaticamente a partire dagli altri attributi dimensionali, l’attributo width viene ignorato

heightMode Come widthMode, ma per l’altezza

ratioMode La modalità di espressione dell’aspect ratio. Può essere

exact: ratioWidth e ratioHeight contengono il numeratore e il denominatore dell’aspect ratio espressi come numero razionale

auto: l’aspect ratio è calcolato automaticamente a partire dagli altri attributi dimensionali

controlChainPosition La posizione relativa di questo elemento rispetto agli altri elementi della controlChain a cui questo elemento appartiene. I componenti di una control chain sono ordinati in base a questo attributo, anche con valori non consecutivi. Non valido se il componente non appartiene ad una control chain

controlChainWeight Il peso relativo di questo componente rispetto agli altri componenti in una catena di tipo weighted. Non valido in altri contesti.

Tabella 4.14 Proprietà di oggetti di tipo UiPhoneControl

La trasformazione di questo oggetto in entità del modello di riferimento è piuttosto complessa. Gli attributi

id, posX, posY, margin* e padding* vengono mappati sugli attributi omonimi della classe UiControl. Gli

attributi defaultWidth, defaultHeight e controlChainWeight vengono mappati su attributi omonimi (tranne

controlChainWeight mappato su weight) della classe UiPhoneControl. Tale classe richiede anche altri

attributi, computati come segue

controlChain è la catena di controlli a cui l’elemento appartiene, e può essere ricavata tramite

controlChainId con una ricerca nel DataStore relativo alle catene di controlli

indexInChain, precedentInChain e followingInChain vengono calcolati inserendo in una lista ordinata

per controlChainPosition tutti gli elementi appartenenti alla stessa catena di controlli

positionConstraint è calcolabile a partire da attributi impostati sugli oggetti della collezione

constraints

dimensionConstraint è calcolabile a partire da attributi presenti in questa definizione

71

Il calcolo di un eventuale dimensionConstraint associato è eseguito nel seguente modo

Se widthMode = heightMode = ratioMode = auto, nessun vincolo viene generato

Se widthMode = exact l’attributo fixedWidth del vincolo è impostato al valore di width di questo

oggetto, e l’attributo widthPercent ad un valore nullo

Se widthMode = percent l’attributo widthPercent del vincolo è impostato al valore di width di

questo oggetto diviso per 100, e l’attributo fixedWidth ad un valore nullo

Se heightMode = exact l’attributo fixedHeight del vincolo è impostato al valore di height di questo

oggetto, e l’attributo heightPercent ad un valore nullo

Se heightMode = percent l’attributo heightPercent del vincolo è impostato al valore di height di

questo oggetto diviso per 100, e l’attributo fixedHeight ad un valore nullo

Se ratioMode = exact l’attributo fixedRatio del vincolo è impostato alla stringa

ratioWidth + “:” + ratioHeight

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_UI_PHONE_CONTROL: crea un nuovo elemento in questo DataStore, inizializzando tutti gli

attributi ai valori predefiniti tranne i seguenti, che vengono letti dall’azione di creazione

o uiPhoneControlType che contiene la definizione del tipo di controllo

o viewControllerId

o controlChainId (se presente)

o id

DELETE_UI_PHONE_CONTROL: elimina un elemento da questo DataStore, dato il suo id (contenuto

nell’azione)

EDIT_UI_PHONE_CONTROL: modifica un elemento di questo DataStore, dato il suo id e le

modifiche da applicare (contenuti nell’azione)

DELETE_VIEW_CONTROLLER: elimina tutti gli elementi da questo DataStore il cui attributo

viewControllerId contenga l’id del viewController eliminato da questa azione

DELETE_CONTROL_CHAIN: elimina tutti gli elementi da questo DataStore il cui attributo

controlChainId contenga l’id del viewController eliminato da questa azione

In base al valore di uiPhoneControlType, altre proprietà possono essere aggiunte a quelle predefinite. Tali

proprietà servono per istanziare la sottoclasse corrispondente alla tipologia di controlli nel modello di

riferimento. I controlli che necessitano di proprietà specifiche sono i seguenti.

AudioRecorder

backgroundType Indica se il controllo contiene la scritta rec (in questo caso ha valore “normal”) o il simbolo del microfono (valore “icon”)

preview Indica quale audioPlayer riproduce l’ultima registrazione effettuata da questo controllo. E’ opzionale, e contiene l’id di tale controllo.

Tabella 4.15 Proprietà aggiuntive di controlli AudioRecorder

Button

textColor Il colore del testo scritto all’interno del pulsante

backgroundColor Il colore di sfondo del pulsante

clickColor Il colore di sfondo del pulsante durante la pressione dell’utente

borderRadius Il raggio dei bordi arrotondati del pulsante Tabella 4.16 Proprietà aggiuntive di controlli Button

72

Card

title Il titolo della card

subtitle Il sottotitolo della card

numActions Il numero di azioni da inserire in calce alla card Tabella 4.17 Proprietà aggiuntive di controlli Card

EditText

textColor Il colore del testo mostrato nel controllo

placeHolder Il testo di default mostrato nel controllo in caso di assenza di testo inserito dall’utente

textSize La dimensione del testo del controllo Tabella 4.18 Proprietà aggiuntive di controlli EditText

GridView

gridType Definisce il tipo di celle inserite nella griglia

simple: ogni cella contiene solo testo

image: ogni cella contiene un’immagine

detailed: ogni cella contiene un’immagine con del testo posizionato come didascalia

Tabella 4.19 Proprietà aggiuntive di controlli GridView

Label

textAlign L’allineamento del testo: a sinistra, a destra o centrato

textColor Il colore del testo

textDecoration Può essere grassetto, corsivo o normale

textSize La dimensione del testo in dip Tabella 4.20 Proprietà aggiuntive di controlli Label

ListView

listViewType Il tipo di celle contenute

simple: solo testo

image: testo con un’immagine

detailed: immagine, titolo e sottotitolo Tabella 4.21 Proprietà aggiuntive di controlli ListView

Map

latitude La latitudine del punto su cui la mappa è centrata

longitude La longitudine del punto su cui la mappa è centrata Tabella 4.22 Proprietà aggiuntive di controlli Map

PhotocameraController

backgroundType Il tipo di contenuto dell’elemento, può essere la scritta Photocamera (valore “normal”) o il simbolo della macchina fotografica (valore “icon”)

preview Opzionale, contiene il valore dell’attributo id del controllo che mostra l’ultima immagine scattata tramite questo controllo

Tabella 4.23 Proprietà aggiuntive di controlli PhotocameraController

VideocameraController

backgroundType Il contenuto del controllo: può essere il testo “videocamera” (valore “normal”) o l’immagine di una videocamera (valore “icon”)

preview Opzionale, contiene il valore dell’attributo id del controllo che mostra l’ultimo video acquisito tramite questo controllo

Tabella 4.24 Proprietà aggiuntive di controlli VideocameraController

73

WebView

htmlFileUri Il percorso (eventualmente di rete) del file HTML da mostrare Tabella 4.25 Proprietà aggiuntive di controlli WebView

containers

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe Container del modello di riferimento. Pur essendo anch’essi istanze di UiPhoneControl, il loro

trattamento è nettamente diverso in quanto essi non sono inseribili o eliminabili direttamente da un

utente, ma vengono generati e distrutti quando un viewController viene inserito come componente di una

scena o rimosso da una scena in cui era stato incluso. Gli oggetti contenuti in questo DataStore, oltre alle

proprietà comuni a tutte le istanze di UiPhoneControl, possiedono le seguenti proprietà particolari

Proprietà Descrizione Indicizzata

linkId Identificativo univoco si

containedViewControllerId Identificativo del viewController contenuto in questo container si

sceneId Identificativo della scena che possiede il parentViewController in cui questo container è inserito

si

type Contiene il tipo di device a cui il parentViewController in cui questo container è inserito

Tabella 4.26 Proprietà aggiuntive di controlli Container

Oltre agli attributi specificati in questa tabella, solo il campo ereditato id è indicizzato per questa classe di

componenti, nessun altro campo è indicizzato. La descrizione di questo tipo di controlli consente

l’esportazione della relazione childViewControllers di scene nel modello di riferimento (Scene).

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

LINK_VIEWCONTROLLER: quando un viewController viene inserito come figlio in una scena, questa

azione viene inviata al modello dei dati. In questo DataStore vengono generati due nuovi container

per la coppia scena – viewController, uno per ciascun parentViewController della scena

UNLINK_VIEWCONTROLLER: quando un viewController viene rimosso dai figli di una scena, i

container relativi alla coppia scena – viewController vengono eliminati da questo DataStore

DELETE_VIEWCONTROLLER: elimina tutti i container associati al viewController eliminato, il cui id è

contenuto sia nell’azione che nel campo containedViewControllerId degli elementi da eliminare da

questo DataStore

DELETE_SCENE: elimina tutti i container associati a parentViewController della scena eliminata, il

cui id è contenuto sia nell’azione che nel campo sceneId degli elementi da eliminare da questo

DataStore

EDIT_CONTAINER: permette la modifica delle proprietà di un container, dato il suo id e l’elenco

delle modifiche da applicare (entrambi letti dall’azione)

sourceTypes

Questa chiave contiene un DataStore dedicato a memorizzare oggetti di supporto alla descrizione di

controlli di tipo AudioPlayer, ImageView e VideoView, ovvero tutti quei controlli dediti alla riproduzione di

elementi multimediali. Gli oggetti di questo DataStore modellano una generica sorgente multimediale. Gli

attributi di questi oggetti nel modello di riferimento sono allocati agli stessi controlli utente. Gli oggetti

contenuti in questo DataStore possiedono le seguenti proprietà

74

Proprietà Descrizione Indicizzata

id Identificativo univoco del controllo a cui questo oggetto è associato si

viewControllerId Identificativo univoco del viewController a cui appartiene il controllo associato a questo oggetto tramite l’attributo id

type Tipologia della sorgente: hardware file, resourceFile o remoteFile

fileUri Il percorso della sorgente, valido solo per resourceFile e remoteFile Tabella 4.27 Proprietà di oggetti di tipo SourceType

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_UI_PHONE_CONTROL: crea un nuovo oggetto in questo DataStore quando il controllo

creato con l’azione è di uno dei tipi che possiedono un elemento multimediale

DELETE_UI_PHONE_CONTROL: elimina da questo DataStore l’oggetto associato al controllo

eliminato con questa azione, se presente

EDIT_SOURCE_TYPE: modifica un oggetto di questo DataStore, dati il suo id e le modifiche da

applicare (lette dall’azione)

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi associati al

viewController eliminato, il cui id è contenuto sia nell’azione che nel campo viewControllerId degli

oggetti da eliminare da questo DataStore

gridViewCells

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe GridViewCell (descritta in GridView e GridViewCell) del modello di riferimento. Gli oggetti contenuti

in questo DataStore possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Nome distintivo della cella

title Il testo mostrato nella cella se la griglia è di tipo simple o detailed

gridViewId Identificativo del controllo di tipo gridView a cui la cella appartiene si

viewControllerId Identificativo del viewController a cui appartiene il controllo di cui alla proprietà gridViewId

si

Tabella 4.28 Proprietà di oggetti di tipo GridViewCell

Il campo title corrisponde all’omonimo campo del modello di riferimento. Il campo id è necessario in

Protocode per stabilire relazioni, ma non viene incluso nel modello. Il campo name è necessario per dare

all’utente un modo facile ed efficace di distinguere diverse istanze di questo oggetto, ma non viene

esportato nel modello. Il campo gridViewId consente di esportare la relazione cells degli oggetti di classe

GridView nel modello di riferimento.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_GRID_VIEW_CELL: crea un nuovo elemento in questo DataStore. I campi sono inizializzati

a partire da valori contenuti nell’azione stessa.

EDIT_GRID_VIEW_CELL: modifica un elemento di questo DataStore, dati il suo id e le modifiche da

applicare (entrambi contenuti nell’azione stessa)

DELETE_GRID_VIEW_CELL: elimina un elemento da questo DataStore, dato il suo id (letto

dall’azione stessa)

75

DELETE_UI_PHONE_CONTROL: elimina tutti gli elementi associati al controllo che è stato eliminato

con questa azione, se presenti. Gli elementi da eliminare hanno l’attributo gridViewId impostato

allo stesso valore dell’id del controllo eliminato

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi associati al

viewController eliminato (ovvero associati a controlli contenuti nel viewController eliminato), il cui

id è contenuto sia nell’azione che nel campo viewControllerId degli oggetti da eliminare da questo

DataStore

listViewCells

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe ListViewCell del modello di riferimento (descritta in ListView e ListViewCell). Gli oggetti contenuti in

questo DataStore possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Nome distintivo della cella

title Titolo della cella

subtitle Sottotitolo della cella (utilizzato solo in liste di tipo detailed)

listViewId Identificativo del controllo di tipo listView a cui l’elemento appartiene si

viewControllerId Identificativo del viewController a cui appartiene il controllo di cui alla proprietà listViewId

si

Tabella 4.29 Proprietà di oggetti di tipo ListViewCell

I campi title e subtitle corrispondono agli omonimi campi del modello di riferimento. Il campo id è

necessario in Protocode per stabilire relazioni, ma non viene incluso nel modello. Il campo name è

necessario per dare all’utente un modo facile ed efficace di distinguere diverse istanze di questo oggetto,

ma non viene esportato nel modello. Il campo listViewId consente di esportare la relazione cells degli

oggetti di classe ListView nel modello di riferimento.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_LIST_VIEW_CELL: crea un nuovo elemento in questo DataStore. I campi sono inizializzati a

partire da valori contenuti nell’azione stessa.

EDIT_LIST_VIEW_CELL: modifica un elemento di questo DataStore, dati il suo id e le modifiche da

applicare (entrambi contenuti nell’azione stessa)

DELETE_LIST_VIEW_CELL: elimina un elemento da questo DataStore, dato il suo id (letto dall’azione

stessa)

DELETE_UI_PHONE_CONTROL: elimina tutti gli elementi associati al controllo che è stato eliminato

con questa azione, se presenti. Gli elementi da eliminare hanno l’attributo listViewId impostato allo

stesso valore dell’id del controllo eliminato

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi associati al

viewController eliminato (ovvero associati a controlli contenuti nel viewController eliminato), il cui

id è contenuto sia nell’azione che nel campo viewControllerId degli oggetti da eliminare da questo

DataStore

constraints

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe PositionConstraint del modello di riferimento. Il modello di vincoli supportato da Protocode è

76

esattamente quello definito nel modello di riferimento, in modo da garantire omogeneità a livello di

capacità espressiva. Gli oggetti contenuti in questo DataStore possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

viewControllerId Identificativo univoco del viewController a cui appartiene il controllo proprietario del vincolo

si

name Nome distintivo del vincolo per essere riconosciuto dall’utente

targetId Identificativo univoco del controllo proprietario del vincolo si

side L’elemento notevole del controllo proprietario a cui è applicato il vincolo

refId Identificativo univoco del controllo di appoggio del vincolo, oppure null se il vincolo si appoggia al padre

refSide L’elemento notevole del controllo di appoggio coinvolto nel vincolo

valid Lo stato di validità del vincolo

valid se il vincolo è valido

invalid_incomplete se qualche parametro del vincolo non è ben definito

invalid_loop se il vincolo è coinvolto in un riferimento circolare

Tabella 4.30 Proprietà di oggetti di tipo Constraint

Questa definizione è facilmente utilizzabile per ricavare un oggetto compatibile con la definizione di

Constraint data nel modello di riferimento. In particolare, se l’attributo valid denota una condizione di non

validità del vincolo, il vincolo non viene esportato nel modello di riferimento. Altrimenti:

la proprietà targetId viene utilizzata per capire a quale controllo il vincolo appartiene

se la proprietà targetId ha valore null, si imposta il flag withParent definito nel modello di

riferimento a true, altrimenti a false

la proprietà referenceElement del modello di riferimento è inizializzata a partire da refId

le proprietà referenceLayoutEdge e layoutEdge sono inizializzate rispettivamente ai valori di refSide

e side

la proprietà name è utilizzata solo per identificare il vincolo all’utente, e non viene esportata

la proprietà id viene mappata sulla proprietà omonima

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_CONSTRAINT: crea un nuovo elemento in questo DataStore, inizializzando le proprietà id,

targetId e viewControllerId con valori contenuti nell’azione, tutte le altre proprietà a valori nulli

tranne valid che viene inizializzata a invalid_incomplete

EDIT_COSTRAINT: modifica un elemento in questo DataStore, noti il suo id e le modifiche da

applicare (entrambi contenuti nell’azione). Ad ogni modifica viene ripetuto il controllo di

completezza per il vincolo modificato e il controllo di ciclicità sull’intero grafo dei vincoli, e lo stato

di validità del controllo modificato viene impostato conseguentemente.

DELETE_CONSTRAINT: elimina un elemento di questo DataStore, noto il suo id (contenuto

nell’azione)

DELETE_UI_PHONE_CONTROL: elimina tutti gli elementi di questo DataStore associati al controllo

utente eliminato, il cui id è contenuto sia nell’azione che nell’attributo refId o targetId degli

elementi da rimuovere

77

DELETE_VIEW_CONTROLLER: elimina tutti gli elementi di questo DataStore associati al

viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId

degli elementi da rimuovere

alertDialogs

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe AlertDialog del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le

seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Nome distintivo per essere identificato dall’utente

title Titolo del messaggio mostrato nella finestra di dialogo

message Contenuto del messaggio mostrato nella finestra di dialogo

viewControllerId Identificativo univoco del viewController in cui la finestra di dialogo è definita

si

Tabella 4.31 Proprietà di oggetti di tipo AlertDialog

Le proprietà id, title e message corrispondono alle omonime del modello di riferimento. La proprietà name

viene utilizzata solo per fornire al modellatore un modo semplice di identificare le diverse istanze di questa

definizione, e non viene quindi esportata nel modello. La proprietà viewControllerId consente di ricostruire

la relazione alertDialogs della classe ViewController del modello di riferimento.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_ALERT_DIALOG: crea un nuovo elemento in questo DataStore. Tutti i campi (tranne id)

sono inizializzati alla stringa vuota.

EDIT_ALERT_DIALOG: modifica un elemento di questo DataStore, dati il suo id e le modifiche da

applicare (entrambi contenuti nell’azione)

DELETE_ALERT_DIALOG: elimina un elemento di questo DataStore, dato il suo id (contenuto

nell’azione)

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi collegati al

viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId

delle istanze da eliminare.

progressDialogs

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe ProgressDialog del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le

seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Nome distintivo per essere identificato dall’utente

title Titolo del messaggio mostrato nella finestra di dialogo

message Contenuto del messaggio mostrato nella finestra di dialogo

spinner Flag che indica se viene mostrata l’animazione dell’attesa

viewControllerId Identificativo univoco del viewController in cui la finestra di dialogo è definita

si

Tabella 4.32 Proprietà di oggetti di tipo ProgressDialog

78

Le proprietà id, title, message e spinner corrispondono alle omonime del modello di riferimento. La

proprietà name viene utilizzata solo per fornire al modellatore un modo semplice di identificare le diverse

istanze di questa definizione, e non viene quindi esportata nel modello. La proprietà viewControllerId

consente di ricostruire la relazione progressDialogs della classe ViewController del modello di riferimento.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_PROGRESS_DIALOG: crea un nuovo elemento in questo DataStore. Tutti i campi (tranne

id) sono inizializzati alla stringa vuota, il campo spinner è inizializzato al valore logico false.

EDIT_PROGRESS_DIALOG: modifica un elemento di questo DataStore, dati il suo id e le modifiche

da applicare (entrambi contenuti nell’azione)

DELETE_PROGRESS_DIALOG: elimina un elemento di questo DataStore, dato il suo id (contenuto

nell’azione)

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi collegati al

viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId

delle istanze da eliminare.

asyncTasks

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe AsyncTask del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le

seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

name Nome distintivo per essere identificato dall’utente

viewControllerId Identificativo univoco del viewController in cui il task asincrono è definito

si

Tabella 4.33 Proprietà di oggetti di tipo AsyncTask

La proprietà name corrisponde all’omonima nel modello di riferimento. La proprietà id è necessaria in

Protocode per esprimere relazioni, ma non appartiene al modello di riferimento e non viene quindi

esportata. La proprietà viewControllerId consente di ricostruire la relazione asyncTasks della classe

ViewController del modello di riferimento.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_ASYNC_TASK: crea un nuovo elemento in questo DataStore. I campi id e viewControllerId

sono valorizzati a partire dall’azione, il campo name contiene un nome basato su un codice

progressivo.

EDIT_ASYNC_TASK: modifica un elemento di questo DataStore, dati il suo id e le modifiche da

applicare (entrambi contenuti nell’azione)

DELETE_PROGRESS_DIALOG: elimina un elemento di questo DataStore, dato il suo id (contenuto

nell’azione)

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi collegati al

viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId

delle istanze da eliminare.

79

navigations

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe Navigation del modello di riferimento, fondamentale per la modellazione della navigazione. Gli

oggetti contenuti in questo DataStore possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

fromSceneId Identificativo dell’oggetto scene da cui origina l’azione di navigazione, null in caso di navigazione a partire dal menù

si

fromViewControllerId Identificativo dell’oggetto viewController da cui origina l’attività di navigazione, null in caso di navigazione a partire dal menù

si

fromControlId Identificativo univoco dell’elemento da cui origina la navigazione, sia esso una voce di menù o un controllo utente

si

toSceneId Identificativo univoco della scene di destinazione dell’azione di navigazione

toViewControllerId Identificativo univoco del viewController di destinazione dell’azione di navigazione, null se l’azione di navigazione è diretta ad una scena e non ad un viewController specifico

Tabella 4.34 Proprietà di oggetti di tipo Navigation

La proprietà id corrisponde all’omonima del modello di riferimento. La proprietà fromControlId consente di

ricostruire la relazione navigations dei vari elementi dotati di capacità di navigazione. La proprietà

fromSceneId consente di ricostruire la relazione contextScene, mentre toSceneId e toViewControllerId

consentono di ricostruire le relazioni destinationScene e destinationViewController.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_NAVIGATION: crea un nuovo oggetto in questo DataStore. Tutte le proprietà del nuovo

oggetto sono inizializzate a partire da parametri contenuti nell’azione stessa

EDIT_NAVIGATION: modifica un oggetto in questo DataStore, noti il suo id e le modifiche da

applicare (entrambi contenuti nell’azione stessa)

DELETE_NAVIGATION: elimina un oggetto da questo DataStore, noto il suo id (contenuto

nell’azione stessa)

DELETE_SCENE: elimina da questo DataStore tutti gli oggetti associati alla scena eliminata con

questa azione, il cui identificativo univoco è contenuto sia nell’azione che nel parametro

fromSceneId o toSceneId degli elementi da eliminare

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli oggetti associati al

viewController eliminato con questa azione, il cui identificativo univoco è contenuto sia nell’azione

che nel parametro fromViewControllerId o toViewControllerId degli elementi da eliminare

DELETE_UI_PHONE_CONTROL: elimina da questo DataStore tutti gli oggetti associati al controllo

utente eliminato con questa azione, il cui identificativo univoco è contenuto sia nell’azione che nel

parametro fromControlId degli elementi da eliminare

80

Design dei dati riguardanti il controller

adapterBindings

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe AdapterBinding del modello di riferimento (§3.3.1). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

Proprietà Descrizione Indicizzata

id Identificativo univoco si

sceneId Identificativo univoco dell’oggetto scene a cui appartiene logicamente l’istanza di adapterBinding

si

name Nome distintivo per l’utente di Protocode

adapterClass La strategia di persistenza a cui l’elemento del model associato a questo oggetto appartiene

isList Indica se questo collegamento punta ad un singolo elemento o ad una lista di elementi

fileId L’identificativo univoco dell’oggetto fileStorageRecord associato a questo adapterBinding, se l’adapterClass è file; null altrimenti

entityId L’identificativo univoco dell’oggetto entity associato a questo adapterBinding, se l’adapterClass è entity; null altrimenti

preferenceId L’identificativo univoco dell’oggetto preferenceRecord associato a questo adapterBinding, se l’adapterClass è preference; null altrimenti

cloudObjectId L’identificativo univoco dell’oggetto cloudObject associato a questo adapterBinding, se l’adapterClass è cloud; null altrimenti

cloudRefPath Significativo solo se adapterClass è cloud, indica il path dell’oggetto puntato nel JSON tree del database

Tabella 4.35 Proprietà di oggetti di tipo AdapterBinding

Gli attributi id, isList, name e cloudRefPath corrispondono alle omonime proprietà definite nel modello di

riferimento. Gli attributi fileId, entityId, preferenceId e cloudObjectId traducono rispettivamente le relazioni

di file, entity, preference e object del modello di riferimento. L’attributo adapterClass corrisponde

all’attributo type nel modello di riferimento, mentre sceneId viene utilizzato per ricostruire la relazione

adapterBindings definita su Scene (§3.3.1).

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_ADAPTER_BINDING: crea un nuovo oggetto in questo DataStore. Tutti gli attributi del

nuovo oggetto sono inizializzati con dati ottenuti dall’azione stessa

DELETE_ADAPTER_BINDING: elimina un oggetto da questo DataStore, dato il suo id (contenuto

nell’azione)

DELETE_SCENE: elimina da questo DataStore tutti gli oggetti associati alla scena eliminata, il cui

identificativo univoco è contenuto nell’azione e nell’attributo sceneId degli elementi da eliminare

modelConnectors

Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della

classe ModelConnector del modello di riferimento (§3.3.2). Gli oggetti contenuti in questo DataStore

possiedono le seguenti proprietà

81

Proprietà Descrizione Indicizzata

id Identificativo univoco si

sceneId Identificativo univoco dell’oggetto scene per cui viene definita l’istanza di modelConnector

si

viewControllerId Identificativo univoco dell’oggetto viewController a cui appartiene il controllo utente a cui l’istanza corrente di modelConnector appartiene logicamente

si

controlId Identificativo univoco dell’oggetto uiPhoneControl a cui appartiene l’istanza corrente di modelConnector

si

adapterId Identificativo univoco dell’oggetto adapterBinding da cui l’istanza corrente di modelConnector riceve i dati

si

keypath La proprietà dell’elemento associato all’adapter che va mappata sulla proprietà del controllo. È valido solo per quegli adapter che connettono un oggetto (un’entità relazionale o un oggetto cloud), altrimenti il suo valore è la stringa “.”.

property La proprietà del controllo connessa al model tramite questo connettore Tabella 4.36 Proprietà di oggetti di tipo ModelConnector

Le proprietà id, property e keypath corrispondono agli omonimi attributi definiti nel modello di riferimento,

mentre la relazione adapter del modello di riferimento è normalizzata utilizzando l’attributo adapterId, e la

relazione modelConnectors di UiPhoneControl (§3.3) è normalizzata con l’attributo controlId. Tutti gli altri

attributi sono necessari per l’implementazione delle azioni di cancellazione ricorsiva.

Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni

CREATE_MODEL_CONNECTOR: crea un nuovo oggetto in questo DataStore. Tutti gli attributi del

nuovo oggetto sono inizializzati con dati contenuti nell’azione

DELETE_MODEL_CONNECTOR: elimina un oggetto da questo DataStore, dato il suo id (contenuto

nell’azione)

DELETE_UI_PHONE_CONTROL: elimina da questo DataStore tutti gli oggetti associati con il

controllo utente eliminato con l’azione, il cui identificativo è contenuto sia nell’azione stessa che

nell’attributo controlId degli elementi da eliminare

DELETE_SCENE: elimina da questo DataStore tutti gli oggetti associati con la scene eliminata con

l’azione, il cui identificativo è contenuto sia nell’azione stessa che nell’attributo sceneId degli

elementi da eliminare

DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli oggetti associati con il

viewController eliminato con l’azione, il cui identificativo è contenuto sia nell’azione stessa che

nell’attributo viewControllerId degli elementi da eliminare

DELETE_ADAPTER_BINDING: elimina da questo DataStore tutti gli oggetti associati con l’istanza di

adapterBinding eliminata con l’azione, il cui identificativo è contenuto sia nell’azione stessa che

nell’attributo adapterId degli elementi da eliminare

82

4.3.3 Connessione tra dati e vista

L’evoluzione dello stato nel contesto di software basati su Redux avviene per mezzo di azioni processate dai

reducer, che computano lo stato successivo a partire dall’azione e dallo stato precedente. Al fine di

collegare l’evoluzione dello stato con le azioni dell’utente, è necessario che le azioni siano inviate in

risposta ad un evento, ovvero ad un’interazione tra utente e applicazione. Questo è possibile grazie ai

componenti di connessione tra stato e vista creati tramite react-redux, che espongono al proprio

sottoalbero funzioni (passate come proprietà) che consentono l’invio di azioni allo stato. I componenti della

vista possono quindi ascoltare gli eventi provenienti dall’utente e utilizzare tali funzioni come risposta ad

essi.

In questo capitolo verranno presentate le diverse tipologie di azioni a cui lo stato di Protocode reagisce, il

loro significato, la loro struttura e i componenti che necessitano della possibilità di utilizzarle.

Dal punto di vista strutturale, un’azione è un oggetto JavaScript puro (senza particolari prototipi se non

Object) con un attributo obbligatorio: l’attributo type che contiene il tipo dell’azione. È possibile definire

poi in modo totalmente arbitrario la struttura dell’oggetto, mantenendo questi due vincoli. Nei capitoli

seguenti, tutti gli attributi definiti sulle azioni sono da considerare obbligatori, a meno che non siano

contrassegnati da un asterisco.

Azioni riguardanti il model

EnablePreferences

Questa azione indica la volontà dell’utente di abilitare la persistenza tramite SharedPreferences

nell’applicazione che sta modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è DefaultPreferencesEditor

EnableFileStorage

Questa azione indica la volontà dell’utente di abilitare la persistenza tramite files nell’applicazione che sta

modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è FileStorageEditor

EnableDatabase

Questa azione indica la volontà dell’utente di abilitare la persistenza tramite database relazionale

nell’applicazione che sta modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è Relational database editor

83

EnableCloudDatabase

Questa azione indica la volontà dell’utente di abilitare la persistenza tramite database cloud

nell’applicazione che sta modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è Cloud database editor

DisablePreferences

Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite SharedPreferences

nell’applicazione che sta modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è DefaultPreferencesEditor

DisableFileStorage

Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite files nell’applicazione che

sta modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è FileStorageEditor

DisableDatabase

Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite database relazionale

nell’applicazione che sta modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è Relational database editor

DisableCloudDatabase

Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite database cloud

nell’applicazione che sta modellando.

Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto

semplice.

L’unico componente che utilizza questa azione è Cloud database editor

CreatePreferenceRecord

Questa azione indica la volontà dell’utente di creare una nuova chiave nel dizionario delle

SharedPreferences.

Questa azione supporta i seguenti attributi particolari

id L’identificativo univoco che sarà associato al nuovo record

key La chiave da associare al nuovo record

value Il valore predefinito del nuovo record

type Il tipo del valore contenuto nel nuovo record Tabella 4.37 Payload dell'azione CreatePreferenceRecord

84

L’unico componente che utilizza questa azione è DefaultPreferences Editor

DeletePreferenceRecord

Questa azione indica la volontà dell’utente di eliminare una chiave dal dizionario delle SharedPreferences.

Questa azione supporta i seguenti attributi particolari

recordId L’identificativo univoco dell’elemento da eliminare Tabella 4.38 Payload dell'azione DeletePreferenceRecord

L’unico componente che utilizza questa azione è DefaultPreferences Editor

CreateFileStorageRecord

Questa azione indica la volontà dell’utente di creare una definizione di un nuovo file nel filesystem

dell’applicazione.

Questa azione supporta i seguenti attributi particolari

id L’identificativo univoco che sarà associato al nuovo file

name Il nome del file

extension L’estensione da associare al nome del file

isTemp Marcatore che indica se il file è temporaneo Tabella 4.39 Payload dell'azione CreateFileStorageRecord

L’unico componente che utilizza questa azione è FileStorage Editor

DeleteFileStorageRecord

Questa azione indica la volontà dell’utente di eliminare la definizione di un file nel filesystem

dell’applicazione.

Questa azione supporta i seguenti attributi particolari

recordId L’identificativo univoco dell’elemento da eliminare Tabella 4.40 Payload dell'azione DeleteFileStorageRecord

L’unico componente che utilizza questa azione è FileStorage Editor

CreateEntity

Questa azione indica la volontà dell’utente di creare una definizione di una nuova entità nel database

relazionale

Questa azione supporta i seguenti attributi particolari

id L’identificativo univoco che sarà associato alla nuova entità

name Il nome distintivo della nuova entità

primaryKey Il nome dell’attributo che espleta il ruolo di chiave primaria Tabella 4.41 Payload dell'azione CreateEntity

L’unico componente che utilizza questa azione è RelationalDatabase Editor

EditEntity

Questa azione indica la volontà dell’utente di modificare la definizione di una entità nel database

relazionale

85

Questa azione supporta i seguenti attributi particolari

entityId L’identificativo univoco dell’entità da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Entity

Tabella 4.42 Payload dell'azione EditEntity

L’unico componente che utilizza questa azione è Entity Editor

DeleteEntity

Questa azione indica la volontà dell’utente di eliminare la definizione di una entità nel database relazionale

Questa azione supporta i seguenti attributi particolari

entityId L’identificativo univoco dell’entità da eliminare Tabella 4.43 Payload dell'azione DeleteEntity

L’unico componente che utilizza questa azione è Entity Editor

CreateEntityAttribute

Questa azione indica la volontà dell’utente di creare una definizione di un nuovo attributo per un’entità nel

database relazionale

Questa azione supporta i seguenti attributi particolari

attributeId L’identificativo univoco che sarà associato al nuovo attributo

entityId L’identificativo univoco dell’entità a cui associare l’attributo

attributeName Il nome distintivo del nuovo attributo

attributeType Il tipo dei dati contenuti nell’attributo Tabella 4.44 Payload dell'azione CreateEntityAttribute

L’unico componente che utilizza questa azione è Entity Editor

DeleteEntityAttribute

Questa azione indica la volontà dell’utente di eliminare la definizione di un attributo di un’entità nel

database relazionale

Questa azione supporta i seguenti attributi particolari

attributeId L’identificativo univoco dell’attributo da eliminare Tabella 4.45 Payload dell'azione DeleteEntityAttribute

L’unico componente che utilizza questa azione è Entity Editor

CreateEntityRelation

Questa azione indica la volontà dell’utente di creare una definizione di una nuova relazione tra due entità

nel database relazionale

Questa azione supporta i seguenti attributi particolari

relationId L’identificativo univoco che sarà associato alla nuova relazione

fromEntityId L’identificativo univoco dell’entità da cui origina la relazione

toEntityId L’identificativo univoco dell’entità di destinazione della relazione

relationName Il nome distintivo della nuova relazione

relationCardinality La cardinalità della nuova relazione Tabella 4.46 Payload dell'azione CreateEntityRelation

86

L’unico componente che utilizza questa azione è Entity Editor

DeleteEntityRelation

Questa azione indica la volontà dell’utente di eliminare la definizione di una relazione tra due entità nel

database relazionale

Questa azione supporta i seguenti attributi particolari

relationId L’identificativo univoco della relazione da eliminare Tabella 4.47 Payload dell'azione DeleteEntityRelation

L’unico componente che utilizza questa azione è Entity Editor

CreateCloudObject

Questa azione indica la volontà dell’utente di creare una definizione di un nuovo oggetto da inserire nel

database cloud

Questa azione supporta i seguenti attributi particolari

objectId L’identificativo univoco che sarà associato alla nuova tipologia di oggetti

objectName Il nome distintivo della nuova tipologia di oggetti Tabella 4.48 Payload dell'azione CreateCloudObject

L’unico componente che utilizza questa azione è CloudDatabase Editor

EditCloudObject

Questa azione indica la volontà dell’utente di modificare la definizione di una tipologia di oggetti nel

database relazionale

Questa azione supporta i seguenti attributi particolari

objectId L’identificativo univoco dell’oggetto da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo CloudObject

Tabella 4.49 Payload dell'azione EditCloudObject

L’unico componente che utilizza questa azione è CloudObject Editor

DeleteCloudObject

Questa azione indica la volontà dell’utente di eliminare la definizione di una tipologia di oggetti dal

database relazionale

Questa azione supporta i seguenti attributi particolari

objectId L’identificativo univoco dell’oggetto da eliminare Tabella 4.50 Payload dell'azione DeleteCloudObject

L’unico componente che utilizza questa azione è CloudObject Editor

CreateCloudObjectAttribute

Questa azione indica la volontà dell’utente di creare una definizione di un nuovo attributo per un’entità nel

database relazionale

87

Questa azione supporta i seguenti attributi particolari

attributeId L’identificativo univoco che sarà associato al nuovo attributo

objectId L’identificativo univoco della tipologia di oggetti a cui associare l’attributo

attributeName Il nome distintivo del nuovo attributo

attributeType Il tipo dei dati contenuti nell’attributo

attributeObject L’identificativo univoco della tipologia di oggetti contenuta nell’attributo, se la tipologia è ref o ref_list

Tabella 4.51 Payload dell'azione CreateCloudObjectAttribute

L’unico componente che utilizza questa azione è CloudObject Editor

DeleteCloudObjectAttribute

Questa azione indica la volontà dell’utente di eliminare la definizione di un attributo da una tipologia di

oggetti nel database relazionale

Questa azione supporta i seguenti attributi particolari

attributeId L’identificativo univoco dell’attributo da eliminare Tabella 4.52 Payload dell'azione DeleteCloudObjectAttribute

L’unico componente che utilizza questa azione è CloudObject Editor

Azioni riguardanti la view

CreateScene

Questa azione indica la volontà dell’utente di creare una nuova scena

Questa azione supporta i seguenti attributi particolari

sceneId L’identificativo univoco che sarà associato alla nuova scena Tabella 4.53 Payload dell'azione CreateScene

L’unico componente che utilizza questa azione è Smartphone & Tablet Editor

EditScene

Questa azione indica la volontà dell’utente di modificare gli attributi di una scena

Questa azione supporta i seguenti attributi particolari

sceneId L’identificativo univoco della scena da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Scene

Tabella 4.54 Payload dell'azione EditScene

L’unico componente che utilizza questa azione è Scene Inspector

DeleteScene

Questa azione indica la volontà dell’utente di eliminare una scena

Questa azione supporta i seguenti attributi particolari

sceneId L’identificativo univoco della scena da eliminare Tabella 4.55 Payload dell'azione DeleteScene

88

L’unico componente che utilizza questa azione è Scene Inspector

LinkViewController

Questa azione indica la volontà dell’utente di utilizzare un viewController nella formazione di una scena

Questa azione supporta i seguenti attributi particolari

linkId L’identificativo univoco che sarà associato alla connessione tra scena e viewController

sceneId Identificativo univoco della scena coinvolta nell’operazione

viewControllerId Identificativo univoco del viewController coinvolto nell’operazione Tabella 4.56 Payload dell'azione LinkViewController

L’unico componente che utilizza questa azione è Scene Inspector

UnlinkViewController

Questa azione indica la volontà dell’utente di smettere di utilizzare un viewController nella formazione di

una scena

Questa azione supporta i seguenti attributi particolari

linkid L’identificativo univoco della connessione tra scena e viewController che deve essere interrotta Tabella 4.57 Payload dell'azione UnlinkViewController

L’unico componente che utilizza questa azione è Scene Inspector

EditContainer

Questa azione indica la volontà dell’utente di modificare gli attributi di una oggetto di tipo container

Questa azione supporta i seguenti attributi particolari

containerId L’identificativo univoco del container da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Container

Tabella 4.58 Payload dell'azione EditContainer

L’unico componente che utilizza questa azione è Container Inspector

CreateViewController

Questa azione indica la volontà dell’utente di creare un nuovo viewController

Questa azione supporta i seguenti attributi particolari

vcId L’identificativo univoco che sarà associato al nuovo viewController Tabella 4.59 Payload dell'azione CreateViewController

L’unico componente che utilizza questa azione è Smartphone & Tablet Editor

EditViewController

Questa azione indica la volontà dell’utente di modificare gli attributi di un viewController

89

Questa azione supporta i seguenti attributi particolari

vcId L’identificativo univoco del viewController da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ViewController

Tabella 4.60 Payload dell'azione EditViewController

L’unico componente che utilizza questa azione è ViewController Inspector

DeleteViewController

Questa azione indica la volontà dell’utente di eliminare un viewController

Questa azione supporta i seguenti attributi particolari

vcId L’identificativo univoco del viewController da eliminare Tabella 4.61 Payload dell'azione DeleteViewController

L’unico componente che utilizza questa azione è ViewController Inspector

CreateUiPhoneControl

Questa azione indica la volontà dell’utente di creare un nuovo controllo utente

Questa azione supporta i seguenti attributi particolari

controlId L’identificativo univoco che sarà associato al nuovo controllo utente

uiPhoneControlType La tipologia di controllo

viewController L’identificativo univoco del viewController in cui il controllo utente è inserito

controlChainId* L’identificativo univoco della control chain in cui il controllo utente è inserito

controlChainPosition* La posizione relativa dell’elemento nella catena Tabella 4.62 Payload dell'azione CreateUiPhoneControl

Componenti che utilizzano questa azione

Scene Editor

ViewController Editor

ControlChain Inspector

EditUiPhoneControl

Questa azione indica la volontà dell’utente di modificare gli attributi di un controllo utente

Questa azione supporta i seguenti attributi particolari

controlId L’identificativo univoco del viewController da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo UiPhoneControl Tabella 4.63 Payload dell'azione EditUiPhoneControl

Componenti che utilizzano questa azione

AudioPlayer Inspector

AudioRecorder Inspector

90

Button Inspector

Card Inspector

DatePicker Inspector

GridView Inspector

ImageView Inspector

Label Inspector

Map Inspector

ListView Inspector

PhotocameraController Inspector

Slider Inspector

Spinner Inspector

Switch Inspector

TextEdit Inspector

TimePicker Inspector

VideoCamera Controller Inspector

VideoView Inspector

WebView Inspector

DeleteUiPhoneControl

Questa azione indica la volontà dell’utente di eliminare un controllo utente

Questa azione supporta i seguenti attributi particolari

controlId L’identificativo univoco del controllo utente da eliminare Tabella 4.64 Payload dell'azione DeleteUiPhoneControl

Componenti che utilizzano questa azione

AudioPlayer Inspector

AudioRecorder Inspector

Button Inspector

Card Inspector

DatePicker Inspector

GridView Inspector

ImageView Inspector

Label Inspector

Map Inspector

ListView Inspector

PhotocameraController Inspector

Slider Inspector

Spinner Inspector

Switch Inspector

TextEdit Inspector

TimePicker Inspector

VideoCamera Controller Inspector

VideoView Inspector

WebView Inspector

91

CreateControlChain

Questa azione indica la volontà dell’utente di creare una nuova control chain

Questa azione supporta i seguenti attributi particolari

chainid L’identificativo univoco che sarà associato alla nuova control chain

viewControllerId L’identificativo univoco del viewController in cui la control chain è inserita Tabella 4.65 Payload dell'azione CreateControlChain

L’unico componente che utilizza questa azione è ViewController Editor

EditControlChain

Questa azione indica la volontà dell’utente di modificare gli attributi di una control chain

Questa azione supporta i seguenti attributi particolari

chainId L’identificativo univoco della control chain da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ControlChain

Tabella 4.66 Payload dell'azione EditControlChain

L’unico componente che utilizza questa azione è ControlChain Inspector

DeleteControlChain

Questa azione indica la volontà dell’utente di eliminare una control chain

Questa azione supporta i seguenti attributi particolari

chainId L’identificativo univoco della control chain da eliminare Tabella 4.67 Payload dell'azione DeleteControlChain

L’unico componente che utilizza questa azione è ControlChain Inspector

CreateConstraint

Questa azione indica la volontà dell’utente di definire un vincolo di posizione applicato ad un componente

Questa azione supporta i seguenti attributi particolari

constraintId L’identificativo univoco che sarà associato al nuovo constraint

viewControllerId L’identificativo univoco del viewController in cui la control chain è inserita

targetId L’identificativo univoco del controllo utente che possiede il vincolo Tabella 4.68 Payload dell'azione CreateConstraint

Componenti che utilizzano questa azione

AudioPlayer Inspector

AudioRecorder Inspector

Button Inspector

DatePicker Inspector

GridView Inspector

ImageView Inspector

Label Inspector

92

Map Inspector

ListView Inspector

PhotocameraController Inspector

Slider Inspector

Spinner Inspector

Switch Inspector

TextEdit Inspector

TimePicker Inspector

VideoCamera Controller Inspector

VideoView Inspector

WebView Inspector

EditConstraint

Questa azione indica la volontà dell’utente di modificare gli attributi di un constraint definito su un

controllo utente

Questa azione supporta i seguenti attributi particolari

constraintId L’identificativo univoco del constraint da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Constraint, ad eccezione di valid

Tabella 4.69 Payload dell'azione EditConstraint

L’unico componente che utilizza questa azione è Constraint Inspector

DeleteConstraint

Questa azione indica la volontà dell’utente di eliminare un vincolo di posizione applicato ad un controllo

utente

Questa azione supporta i seguenti attributi particolari

constraintId L’identificativo univoco del vincolo da eliminare Tabella 4.70 Payload dell'azione DeleteConstraint

L’unico componente che utilizza questa azione è Constraint Inspector

CreateMenuItem

Questa azione indica la volontà dell’utente di definire un inserire una nuova voce di menu

Questa azione supporta i seguenti attributi particolari

menuItemId L’identificativo univoco che sarà associato al nuovo elemento del menu

title Il titolo dell’elemento di menu Tabella 4.71 Payload dell'azione CreateMenuItem

L’unico componente che utilizza questa azione è Menu Inspector

EditMenuItem

Questa azione indica la volontà dell’utente di modificare gli attributi di una voce di menu

93

Questa azione supporta i seguenti attributi particolari

menuItemId L’identificativo univoco della voce di menu da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo MenuItem

Tabella 4.72 Payload dell'azione EditMenuItem

L’unico componente che utilizza questa azione è MenuItem Inspector

DeleteMenuItem

Questa azione indica la volontà dell’utente di eliminare una voce di menu

Questa azione supporta i seguenti attributi particolari

menuItemId L’identificativo univoco della voce di menu da eliminare Tabella 4.73 Payload dell'azione DeleteMenuItem

L’unico componente che utilizza questa azione è MenuItem Inspector

CreateGridViewCell

Questa azione indica la volontà dell’utente di definire un inserire una nuova cella in un controllo utente di

tipo griglia

Questa azione supporta i seguenti attributi particolari

cellId L’identificativo univoco che sarà associato alla nuova cella

name Il nome distintivo della cella

gridViewId L’identificativo univoco del controllo utente a cui appartiene la cella

viewControllerId L’identificativo univoco del viewController a cui appartiene il controllo utente che contiene la cella

Tabella 4.74 Payload dell'azione CreateGridViewCell

L’unico componente che utilizza questa azione è GridView Inspector

EditGridViewCell

Questa azione indica la volontà dell’utente di modificare gli attributi di una cella di una griglia

Questa azione supporta i seguenti attributi particolari

cellId L’identificativo univoco della cella da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo GridViewCell

Tabella 4.75 Payload dell'azione EditGridViewCell

L’unico componente che utilizza questa azione è GridViewCell Inspector

DeleteGridViewCell

Questa azione indica la volontà dell’utente di eliminare una cella di una griglia

Questa azione supporta i seguenti attributi particolari

cellId L’identificativo univoco della cella da eliminare Tabella 4.76 Payload dell'azione DeleteGridViewCell

94

L’unico componente che utilizza questa azione è GridViewCell Inspector

CreateListViewCell

Questa azione indica la volontà dell’utente di definire un inserire una nuova cella in un controllo utente di

tipo lista

Questa azione supporta i seguenti attributi particolari

cellId L’identificativo univoco che sarà associato alla nuova cella

name Il nome distintivo della cella

gridViewId L’identificativo univoco del controllo utente a cui appartiene la cella

viewControllerId L’identificativo univoco del viewController a cui appartiene il controllo utente che contiene la cella

Tabella 4.77 Payload dell'azione CreateListViewCell

L’unico componente che utilizza questa azione è ListView Inspector

EditListViewCell

Questa azione indica la volontà dell’utente di modificare gli attributi di una cella di una lista

Questa azione supporta i seguenti attributi particolari

cellId L’identificativo univoco della cella da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ListViewCell

Tabella 4.78 Payload dell'azione EditListViewCell

L’unico componente che utilizza questa azione è ListViewCell Inspector

DeleteListViewCell

Questa azione indica la volontà dell’utente di eliminare una cella di una lista

Questa azione supporta i seguenti attributi particolari

cellId L’identificativo univoco della cella da eliminare Tabella 4.79 Payload dell'azione DeleteListViewCell

L’unico componente che utilizza questa azione è ListViewCell Inspector

CreateNavigation

Questa azione indica la volontà dell’utente di definire un’azione di navigazione per un controllo utente nel

contesto di una scena

Questa azione supporta i seguenti attributi particolari

navigationId L’identificativo univoco che sarà associato all’azione di navigazione

fromSceneId L’identificativo univoco della scena da cui origina la navigazione, se esiste

fromViewControllerId L’identificativo univoco del viewController da cui origina la navigazione, se esiste

fromControlId L’identificativo univoco del controllo utente o della voce di menu a cui è associata l’azione di navigazione

toSceneId L’identificativo univoco della scena di destinazione dell’azione di navigazione

toViewControllerId L’identificativo univoco del viewController di destinazione dell’azione di navigazione, se esiste

Tabella 4.80 Payload dell'azione CreateNavigation

95

Componenti che utilizzano questa azione

Button Inspector

GridView Inspector

ListView Inspector

MenuItem Inspector

EditNavigation

Questa azione indica la volontà dell’utente di modificare gli attributi di un’azione di navigazione definita su

un controllo utente nel contesto di una scena

Questa azione supporta i seguenti attributi particolari

navigationId L’identificativo univoco dell’azione di navigazione da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Navigation

Tabella 4.81 Payload dell'azione EditNavigation

Componenti che utilizzano questa azione

Button Inspector

GridView Inspector

ListView Inspector

MenuItem Inspector

DeleteNavigation

Questa azione indica la volontà dell’utente di eliminare un’azione di navigazione associata ad un controllo

utente nel contesto di una scena

Questa azione supporta i seguenti attributi particolari

navigationId L’identificativo univoco dell’azione di navigazione da eliminare Tabella 4.82 Payload dell'azione DeleteNavigation

Componenti che utilizzano questa azione

Button Inspector

GridView Inspector

ListView Inspector

MenuItem Inspector

EditSourceType

Questa azione indica la volontà dell’utente di modificare gli attributi di una sorgente multimediale associata

ad un controllo utente

Questa azione supporta i seguenti attributi particolari

navigationId L’identificativo univoco dell’azione di navigazione da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo SourceType

Tabella 4.83 Payload dell'azione EditSourceType

96

Componenti che utilizzano questa azione

AudioPlayer Inspector

ImageView Inspector

VideoView Inspector

CreateAlertDialog

Questa azione indica la volontà dell’utente di definire un alert dialog in un viewController

Questa azione supporta i seguenti attributi particolari

dialogId L’identificativo univoco che sarà associato all’alert dialog

viewControllerId L’identificativo univoco del viewController in cui l’alert dialog viene definito Tabella 4.84 Payload dell'azione CreateAlertDialog

L’unico componente che utilizza questa azione è ViewController Inspector

EditAlertDialog

Questa azione indica la volontà dell’utente di modificare gli attributi di un alert dialog

Questa azione supporta i seguenti attributi particolari

dialogId L’identificativo univoco dell’alert dialog da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo AlertDialog

Tabella 4.85 Payload dell'azione EditAlertDialog

L’unico componente che utilizza questa azione è AlertDialog Inspector

DeleteAlertDialog

Questa azione indica la volontà dell’utente di eliminare un alert dialog

Questa azione supporta i seguenti attributi particolari

dialogId L’identificativo univoco dell’alert dialog da eliminare Tabella 4.86 Payload dell'azione DeleteAlertDialog

L’unico componente che utilizza questa azione è AlertDialog Inspector

CreateProgressDialog

Questa azione indica la volontà dell’utente di definire un progress dialog in un viewController

Questa azione supporta i seguenti attributi particolari

dialogId L’identificativo univoco che sarà associato al progress dialog

viewControllerId L’identificativo univoco del viewController in cui il progress dialog viene definito Tabella 4.87 Payload dell'azione CreateProgressDialog

L’unico componente che utilizza questa azione è ViewController Inspector

EditProgressDialog

Questa azione indica la volontà dell’utente di modificare gli attributi di un progress dialog

97

Questa azione supporta i seguenti attributi particolari

dialogId L’identificativo univoco del progress dialog da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ProgressDialog

Tabella 4.88 Payload dell'azione EditProgressDialog

L’unico componente che utilizza questa azione è ProgressDialog Inspector

DeleteProgressDialog

Questa azione indica la volontà dell’utente di eliminare un progress dialog

Questa azione supporta i seguenti attributi particolari

dialogId L’identificativo univoco del progress dialog da eliminare Tabella 4.89 Payload dell'azione DeleteProgressDialog

L’unico componente che utilizza questa azione è ProgressDialog Inspector

CreateAsyncTask

Questa azione indica la volontà dell’utente di definire un async task in un viewController

Questa azione supporta i seguenti attributi particolari

taskId L’identificativo univoco che sarà associato all’async task

viewControllerId L’identificativo univoco del viewController in cui l’async task viene definito Tabella 4.90 Payload dell'azione CreateAsyncTask

L’unico componente che utilizza questa azione è ViewController Inspector

EditAsyncTask

Questa azione indica la volontà dell’utente di modificare gli attributi di un async task

Questa azione supporta i seguenti attributi particolari

taskId L’identificativo univoco dell’async task da modificare

edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo AsyncTask

Tabella 4.91 Payload dell'azione EditAsyncTask

L’unico componente che utilizza questa azione è AsyncTask Inspector

DeleteAsyncTask

Questa azione indica la volontà dell’utente di eliminare un async task

Questa azione supporta i seguenti attributi particolari

taskId L’identificativo univoco dell’alert dialog da eliminare Tabella 4.92 Payload dell'azione DeleteAsyncTask

L’unico componente che utilizza questa azione è AsyncTask Inspector

98

CreateApplication

Questa azione indica la volontà dell’utente di creare una nuova applicazione.

Questa azione non necessita di particolari attributi

L’unico componente che utilizza questa azione è Navbar

ChangeName

Questa azione indica la volontà dell’utente di rinominare l’applicazione corrente.

name Il nuovo nome dell’applicazione Tabella 4.93 Payload dell'azione ChangeName

L’unico componente che utilizza questa azione è ManifestEditor

ChangeCompanyIdentifier

Questa azione indica la volontà dell’utente di cambiare l’identificativo del produttore per l’applicazione

corrente.

companyIdentifier Il nuovo nome identificativo del produttore per l’applicazione corrente. Tabella 4.94 Payload dell'azione ChangeCompanyIdentifier

L’unico componente che utilizza questa azione è ManifestEditor

DeleteApplication

Questa azione indica la volontà dell’utente di eliminare l’applicazione che si sta sviluppando

Questa azione non necessita di particolari attributi

L’unico componente che utilizza questa azione è Navbar

Azioni riguardanti il controller

CreateAdapterBinding

Questa azione indica la volontà dell’utente di creare un adapterBinding per una scena

Questa azione supporta i seguenti attributi particolari

adapterId L’identificativo univoco che sarà associato all’adapterBinding

scene L’identificativo univoco della scena per cui l’adapterBinding viene definito

adapterClass Il tipo di persistenza con cui l’adapterBinding si interfaccia

file L’identificativo univoco del file da cui i dati vengono letti, se adapterClass è file

entity L’identificativo univoco dell’entità di cui vengono estratte una o più istanze, se adapterClass è entity

preference L’identificativo univoco del record del dizionario delle preferenze da cui i dati vengono estratti, se adapterClass è preference

cloudObject L’identificativo univoco della tipologia di oggetti cloud di cui vengono estratte una o più istanze se adapterClass è cloud

isList Indica se l’adapterBinding estrae una sola entità o una lista di esse

name Il nome distintivo dell’adapterBinding

cloudRefPath Il percorso delle entità da estrarre, nel caso in cui adapterClass sia cloud Tabella 4.95 Payload dell'azione CreateAdapterBinding

99

L’unico componente che utilizza questa azione è Scene Model Editor

DeleteAdapterBinding

Questa azione indica la volontà dell’utente di eliminare un adapterBinding

Questa azione supporta i seguenti attributi particolari

adapterId L’identificativo univoco dell’adapterBinding da eliminare Tabella 4.96 Payload dell'azione DeleteAdapterBinding

L’unico componente che utilizza questa azione è Scene Model Editor

CreateModelConnector

Questa azione indica la volontà dell’utente di creare un modelConnector tra una proprietà di un controllo

utente in una scena e un adapterBinding

Questa azione supporta i seguenti attributi particolari

connectorId L’identificativo univoco che sarà associato al modelConnector

sceneId L’identificativo univoco della scena in cui il modelConnector è valido

viewControllerId L’identificativo univoco del viewController a cui appartiene il controllo a cui è associato il modelConnector

controlId L’identificativo univoco del controllo utente a cui appartiene il modelConnector

adapterId L’identificativo univoco dell’adapterBinding da cui il modelConnector ottiene i dati

keypath Il percorso all’interno della struttura esposta dall’adapterBinding da cui vengono estratti i dati esposti al controllo utente tramite il modelConnector

property La proprietà del controllo che viene collegata al modello tramite il modelConnector

Tabella 4.97 Payload dell'azione CreateModelConnector

Componenti che utilizzano questa azione

AudioPlayer Inspector

Button Inspector

Card Inspector

DatePicker Inspector

GridView Inspector

ImageView Inspector

Label Inspector

Map Inspector

ListView Inspector

Slider Inspector

Spinner Inspector

Switch Inspector

TextEdit Inspector

TimePicker Inspector

VideoView Inspector

WebView Inspector

100

DeleteModelConnector

Questa azione indica la volontà dell’utente di eliminare un modelConnector

Questa azione supporta i seguenti attributi particolari

connectorId L’identificativo univoco del modelConnector da eliminare Tabella 4.98 Payload dell'azione DeleteModelConnector

Componenti che utilizzano questa azione

AudioPlayer Inspector

Button Inspector

Card Inspector

DatePicker Inspector

GridView Inspector

ImageView Inspector

Label Inspector

Map Inspector

ListView Inspector

Slider Inspector

Spinner Inspector

Switch Inspector

TextEdit Inspector

TimePicker Inspector

VideoView Inspector

WebView Inspector

101

4.3.4 Design della navigazione

Questa sezione presenta la struttura di Protocode dal punto di vista della navigazione, dettagliando le

pagine disponibili e le modalità per accedervi

Gli editor di primo livello sono immediatamente accessibili dalla barra di navigazione dell’applicazione, e

riflettono la categorizzazione concettuale del modello di Protocode: editor del modello, editor della vista,

editor del manifesto dell’applicazione e editor dei modelli delle scene. È inoltre presente una schermata di

presentazione di Protocode, con una breve descrizione e i link ai repository di tutte le versioni con i nomi

dei rispettivi autori.

L’editor delle scene è a sua volta diviso, tramite un layout a schede, in editor per cellulari e tablet ed editor

per smartwatch. L’editor per smartwatch è incluso nello schema per completezza, ma non verrà discusso in

quanto non rilevante ai fini di questo lavoro di tesi.

I seguenti paragrafi dettagliano la struttura degli editor più complessi. Manifest Editor e Scene Model Editor

sono costituiti da una sola schermata e non verranno ulteriormente dettagliati in questa sezione.

Smartphone & Tablet Editor

Figura 4.6 Navigazione dell'editor dedicato alla View

Figura 4.5 Navigazione di alto livello

102

L’editor per la parte di view per smartphone e tablet è caratterizzato da varie schermate. Procedendo

gerarchicamente, troviamo gli editor per scene e viewControllers. Le scene sono gli elementi di primo livello

nel modello dell’applicazione mobile alla base di Protocode, mentre i viewController, pur essendo

componenti delle scene, hanno una dignità propria in quanto definibili a priori rispetto alle scene

(l’aggregazione è solo un passo successivo).

L’editor delle scene permette la modifica della struttura delle scene, intesa come la selezione dei

componenti da aggregare per la definizione della scena stessa. Inoltre, esso permette l’accesso all’editor

del menu principale, all’editor di ciascuno dei view controller uniti a comporre la scena e ai view controller

responsabili dell’organizzazione dei precedenti sullo schermo (in caso di scena MultiVC).

L’editor del menù è riducibile ad una lista di elementi, ciascuno modificabile con un editor dedicato agli

elementi del menù.

L’editor per view controller, oltre a permettere di modificare le proprietà di un view controller, permette di

accedere ad un editor diverso per ciascuna tipologia di controlli utente. I controlli inseriti sono elencati nel

prospetto di struttura del view controller e mostrati nella preview dell’applicazione: selezionando un

controllo è possibile accedere all’editor dedicato. Da ogni editor riservato ad un controllo è possibile

accedere all’editor dei vincoli per quel controllo (tale editor è inserito in una scheda dell’editor

precedente).

L’editor delle controlChain permette di personalizzare i parametri di ciascuna controlChain, di aggiungere o

rimuovere componenti della control chain e di impostare i pesi dei diversi componenti (per catene

weighted). Esso inoltre da accesso, esattamente come l’editor per view controller, agli editor per i vari

controlli inseriti.

Tra i vari editor per controlli utente, l’editor per liste e l’editor per griglie danno accesso ciascuno ad un

ulteriore editor: nel caso in cui le celle non siano dinamicamente create a partire da valori del modello, ma

definite staticamente, è possibile personalizzarne titolo e contenuto in tali editor accessori.

Model Editor

Figura 4.7 Navigazione dell'editor dedicato al model

Il model editor è costituito da quattro editor, uno per ogni strategia di persistenza. La navigazione tra i vari

editor avviene tramite un layout master-detail.

Gli editor dedicati al database relazionale e al database cloud si avvantaggiano ciascuno di una ulteriore

schermata. Tali schermate sono dedicate rispettivamente alle entità del database relazionale a agli oggetti

cloud. La navigazione in entrambi i casi si basa nuovamente sul pattern master-detail, dove il master

103

contiene un indice degli oggetti (nella fattispecie entità e oggetti cloud) e il detail permette la modifica

dell’oggetto selezionato

4.3.5 Design dell’interfaccia utente

Questa sezione descrive la struttura dell’interfaccia utente di Protocode. Essendo un’applicazione basata su

React e sulla libreria ReactRouter per la navigazione, l’interfaccia utente è costruita per una gran parte in

modo gerarchico: ogni componente riserva una porzione della propria superficie sullo schermo per inserire

un componente figlio. Questo permette di ottenere una mappatura delle URL delle varie schermate su un

albero di componenti da visualizzare: ogni segmento dell’URL è un componente (o un parametro da passare

al componente precedente), e la parte dell’URL che segue ciascun segmento descrive i componenti figli.

Il contenuto di ogni schermata varia molto in base alla funzione della schermata stessa. L’unico elemento

sempre visibile è la barra di navigazione che consente di spostarsi tra i vari editor, e di accedere a

funzionalità di impatto globale sull’applicazione quali la creazione, la cancellazione o il download di

un’applicazione.

I seguenti capitoli presentano le interfacce utente destinate a Model Editor, Scene Model Editor e Scene

Editor. L’interfaccia relativa al manifest editor è costituita da un solo form, e non verrà ulteriormente

discussa. L’interfaccia destinata allo Smartwatch Editor è costruita esattamente come quella destinata al

Scene Editor, e verrà discussa insieme a quest’ultima in questa sezione.

Model Editor

Figura 4.8 Interfaccia utente per l'editor del modello

L’interfaccia relativa al model editor, oltre alla barra di navigazione, si basa sul pattern master-detail: il

master permette la selezione della strategia di storage su cui si desidera lavorare, il detail consente la

configurazione della strategia selezionata.

104

Scene Editor

Figura 4.9 Interfaccia utente per l'editor della View

Questa interfaccia presenta alcuni componenti fissi e altri variabili in base all’URL corrente, ovvero al

componente selezionato.

Lo screen navigator è un elemento costante che consente la navigazione tra la varie scene e tra i vari view

controller attraverso un menu a tendina. La selezione di una scena o di un view controller dal menù causa

un cambio dell’URL e in conseguenza della schermata.

Il report mostra eventuali problemi relativi a posizionamento o definizione di elementi legati al

componente selezionato. Nel caso si stia lavorando su una scena, vengono esaminati tutti gli elementi

inclusi, dai view controller ai controlli utente.

La device preview mostra un’anteprima dell’applicazione nella cornice di un device selezionabile da una

lista (il device selector), permette l’aggiunta di componenti trascinati dalla palette e la navigazione tra gli

editor dei vari componenti. Il componente selezionato è evidenziato da un bordo e i vincoli di posizione o di

dimensione sono evidenziati attraverso punti o segmenti disegnati intorno al componente stesso. Per i

vincoli di posizione (constraints) vengono mostrati anche i punti di riferimento.

L’inspector è il componente più dinamico, e varia molto in base all’elemento attualmente selezionato. Le

schermate destinate all’editing dei vari controlli si ottengono combinando l’inspector progettato per la

tipologia di controlli stessa e i componenti descritti precedentemente in questo capitolo. Questo garantisce

un look and feel consistente e mantiene sempre disponibile l’anteprima della vista sul video, in modo da

garantire un feedback istantaneo sul lavoro che si sta facendo. Generalmente presenta un layout a schede,

ciascuna delle quali assolve un compito particolare. Le schede nella maggior parte dei casi sono

proprietà specifiche

vincoli di posizione

dimensioni

margini e padding

connessioni con il model

Alcune di esse possono essere omesse per alcuni componenti.

105

Anche le interfacce destinate all’editing di constraints, control chains, celle appartenenti a liste o griglie e

del menù stesso sono gestite da questo componente attraverso inspector adatti alla tipologia di elemento

che si va a modificare.

Scene Model Editor

Figura 4.10 Interfaccia utente per il scene model editor

La schermata destinata al Scene Model Editor presenta una navigazione master – detail: il master permette

la selezione di una scena, mentre il detail bipartito mostra gli elementi attualmente inclusi nel modello

della stessa e gli elementi disponibili per l’inclusione. La scelta di questo tipo di interfaccia deriva dalla sua

larga diffusione: applicazioni come Microsoft Office utilizzano proprio questa modalità per gestire

operazioni di definizioni di sottoinsiemi.

106

4.4 Output della prototipazione Uno dei punti che rendono unica l’applicazione Protocode è la possibilità di esportare il modello

dell’applicazione in formato strutturato basato su XML. Questo abilita la possibilità di utilizzare il prototipo

come base per la generazione automatica del codice dell’applicazione.

Il file che viene esportato da Protocode è costruito attraverso la mappatura di ciascun elemento su un tag

XML che identifica la tipologia di elemento stesso. Gli attributi dell’elemento vengono inseriti come

attributi dell’elemento XML, mentre per quanto riguarda le relazioni vi sono due diverse modalità di

traduzione

inclusione gerarchica: generalmente utilizzata per le relazioni di composizione, prevede che gli

elementi XML che traducono elementi logicamente contenuti in un padre vengano inseriti nel file

come elementi XML figli dell’elemento XML relativo al padre

riferimento: generalmente utilizzata per le relazioni di associazione, prevedono la traduzione della

relazione con un attributo, dal lato da cui la relazione è accessibile, che contiene il percorso

all’interno dell’albero XML dell’elemento associato

La struttura XML viene prodotta utilizzando le regole di corrispondenza tra modello dei dati di Protocode e

modello di riferimento che sono state dettagliate nel paragrafo 4.3.2 Design dei dati, in modo che il

risultato finale sia conforme alle definizione date dal modello di riferimento stesso.

4.5 Struttura del codice sorgente Questa sezione descrive l’organizzazione, in termini di directory, del codice sorgente di Protocode

Le cartelle esterne alla cartella src sono gestite

dall’ambiente React e dal gestore di pacchetti,

l’intera implementazione si trova nella cartella src.

Il file relativi a Redux contengono le azioni, i

reducers e gli strumenti per denormalizzare lo stato,

contenuti nella directory selectors.

Gli editor sono così mappati

Model Editor dataModel/ Manifest Editor manifest/ Scene Model Editor modelAdapter/ Scene Editor editor/scene/ Smartwatch Editor editor/watch

I file nella cartella common sono condivisi tra Scene

Editor e Smartwatch Editor (non trattato in questo

lavoro di tesi).

La logica di generazione del file XML di output è

contenuta nella cartella xml.

La definizione dei diversi tipi di device supportati è

contenuta nella cartella devices Figura 4.11 Organizzazione del codice di Protocode

107

5 MobileCodeGenerator

5.1 Introduzione MobileCodeGenerator è un generatore di codice per applicazioni mobili. Esso consente di generare, almeno

in parte, il codice sorgente di un’applicazione mobile a partire da un modello dell’applicazione stessa. Il

modello di riferimento adottato nell’implementazione sviluppata per questo lavoro di tesi è stato descritto

nel capitolo §3. Ne consegue che l’applicazione generata segue il pattern architetturale MVC che è stato

adottato come base nella definizione del modello stesso, e che il codice che è possibile generare ha la

stessa capacità espressiva del modello. Si evidenziano quindi da subito alcune limitazioni che il modello

stesso pone a questo strumento, prima delle quali l’impossibilità di generare automaticamente i

componenti incaricati di interagire con un eventuale backend. Al fine di ovviare a queste limitazioni e

favorire la possibilità per uno sviluppatore di integrare il codice generato per supplire alle stesse, la

generazione di codice è stata ingegnerizzata in modo da essere il più flessibile possibile.

Dato che il modello di riferimento di MobileCodeGenerator coincide con quello adottato nel design di

Protocode, è possibile affermare che MobileCodeGenerator costituisce la conseguenza logica di Protocode.

Questo permette ai due prodotti di costituire una pipeline per la generazione automatica di applicazioni

mobili basata su modelli.

Come per Protocode, anche MobileCodeGenerator non è interamente frutto di questo lavoro di tesi: la

prima versione è nata nel 2012 dal lavoro di Gregorio Perego e Stefania Pezzetti [3], ed è stata

successivamente migliorata da Mattia Natali nel 2013 [4]. Nell’A.A. 2015/2016 Aldo Pintus [5] ha aggiunto

la possibilità di modellare l’interfaccia per la componente smartwatch (che non tratteremo in questo lavoro

di tesi), mentre nell’A.A. 2017/2018 Alessio Rossotti [6] ha introdotto la modellazione e la generazione del

codice relativi alla parte Model, portando il tool alla versione 4.0. In sintesi, prima di questo lavoro di tesi

MobileCodeGenerator permetteva la generazione automatica delle componenti model e view per

applicazioni native Android e iOS, rispettivamente generate nei linguaggi Java e Swift.

Con questo lavoro di tesi è stata sviluppata la versione 5.0 di MobileCodeGenerator, con due importanti

interventi: l’implementazione di un generatore per applicazioni cross–platform e l’implementazione di un

approccio alla generazione della componente controller. Data l’importanza assunta negli ultimi anni da

ReactNative come strumento per la realizzazione di applicazioni mobili cross – platform, questo lavoro di

tesi investiga sulle potenzialità di tale framework e su come sia possibile sfruttarlo per una generazione

automatica di codice basata su un modello. Inoltre, al fine di mantenere la coerenza con i miglioramenti

apportati al modello di riferimento, è stata introdotta, nel contesto della generazione di codice cross-

platform, la generazione anche delle parti del controller che il modello permette di descrivere. La validità di

questo approccio verrà investigata nel capitolo §6, mentre nel seguito di questo capitolo verrà descritta

l’implementazione di MobileCodeGenerator.

5.2 Software e framework utilizzati MobileCodeGenerator è un generatore di codice basato su Eclipse Modeling Framework e su

OpenArchitectureWare. In quanto tale, la generazione del codice procede attraverso l’espansione ricorsiva

di template.

108

5.2.1 Eclipse Modeling Framework

L’Eclipse Modeling Framework [36] contiene alcuni strumenti utilizzati indirettamente dal generatore di

codice in quanto destinati alla definizione formale del modello di riferimento dell’applicazione che viene

generata.

Lo scopo principale di questo framework è fornire strumenti per la creazione di meta-modelli, ovvero

strumenti destinati a descrivere in modo formale e non ambiguo la struttura di un modello. Tale definizione

può essere progettata con una duplice finalità: di validazione e di deserializzazione di un modello. La

funzionalità di validazione è intuitiva: se il meta-modello descrive la struttura del modello, è possibile

verificare se un’istanza del modello sia conforme alla struttura stessa, e quindi se il modello sia valido

almeno da un punto di vista strutturale. Qualora la validazione strutturale non fosse sufficiente, è possibile

integrare questo framework con strumenti di validazione più attenti alla semantica, come ad esempio lo

strumento Check fornito da OpenArchitectureWare e descritto nel paragrafo successivo. La funzionalità di

deserializzazione invece è estremamente interessante: un modello valido è rappresentato da un albero,

codificato all’interno di un file utilizzando il linguaggio XML. Tale rappresentazione è tuttavia poco pratica

per un generatore di codice. Grazie alla definizione del meta-modello è possibile trasformare ciascun

oggetto definito nel modello in un’istanza di una classe Java i cui attributi sono istanziati a partire dal

modello stesso. Il framework supporta anche la risoluzione di relazioni tra le classi, che sono di

fondamentale importanza nel modello descritto. Sfruttando questa possibilità è possibile implementare il

generatore di codice come un motore basato sull’espansione di templates il cui contesto è l’oggetto che

rappresenta la radice dell’albero descritto tramite XML di cui sopra. Ogni template può, quindi, accedere a

tutti i dati del modello, permettendo così la realizzazione di componenti complessi.

5.2.2 OpenArchitectureWare

OpenArchitectureWare è nato come progetto open source su SourceForge.net e conteneva gli strumenti

Xtext, Xpand e Modeling Workflow Engine (MWE), destinati a costituire una toolbox per l’implementazione

di trasformazioni di modelli in codice eseguibile, anche a supporto della definizione di linguaggi specifici di

un dominio; successivamente, è stato spostato su Eclipse.org e lo sviluppo di ciascun progetto è poi

proseguito in modo indipendente. Per la realizzazione di MobileCodeGenerator sono stati utilizzati Xpand e

MWE: il primo contiene un motore di espansione di templates, necessario per la definizione della logica di

espansione del modello in codice; il secondo permette la definizione e la successiva esecuzione di un file di

workflow che specifica l’elenco ordinato delle operazioni che il motore di traduzione deve eseguire per

generare il codice. Questo consente di realizzare trasformazioni complesse sfruttando un approccio

dichiarativo e modulare: diversi componenti possono essere condivisi tra diversi workflows, favorendo così

un riuso di componenti poco accoppiati. Inoltre, MWE consente l’integrazione con strumenti di post

processing, destinati, ad esempio, alla formattazione del codice generato per una più agevole lettura e

modifica da parte di uno sviluppatore. XPand contiene a sua volta diversi strumenti:

XPand language: è lo strumento principale, serve a definire le regole da applicare per eseguire la

traduzione del modello in codice.

Xtend: dal momento che Xpand language ha un’espressività basilare e limitata, attraverso Xtend è

possibile definire classi e metodi aggiuntivi sfruttando tutta la potenza del linguaggio Java e poi

riutilizzarli nei templates per eseguire operazioni complesse

Check: questo strumento permette di definire controlli semantici da eseguire sul file di modello,

dopo che questo ha passato la validazione strutturale del meta-modello. Come XPand language, le

sue capacità espressive sono piuttosto limitate ma possono essere integrate ed estese con lo

strumento XTend

109

5.3 Struttura del generatore

5.3.1 Struttura preesistente

La versione 4.0 di MobileCodeGenerator supporta la generazione di applicazioni native per iOS e Android.

La struttura del generatore riflette questa dualità, e si presenta costituita da due set di templates, da un

orchestratore che implementa il workflow di generazione, e da alcuni componenti comuni. Di seguito

vengono presentate le varie categorie di templates.

iOS

ios_viewcontrollers_templates: contiene i templates per la generazione del codice relativo ai

controlli utente che poi vengono assemblati a formare i diversi view controllers componenti

l’applicazione

ios_watchcontrollers_templates: contiene i templates per la generazione del codice relativo ai

controlli utente che poi vengono assemblati a formare le diverse schermate di una applicazione per

smartwatch

ios_templates: contiene i templates necessari alla generazione del modello e della persistenza, i

templates che generano la StoryBoard e i templates destinati alla generazione dei files di

descrizione del progetto, come ad esempio il file .xcodeproj necessario all’importazione del

progetto in XCode per la compilazione

ios_extensions: contiene le estensioni scritte in XTend e utilizzate dai templates dei precedenti

gruppi

Android

android_activities_templates: contiene i templates per la generazione del codice relativo ai

controlli utente che poi vengono assemblati a formare i diversi view controllers componenti

l’applicazione

androidwear_activities_templates: contiene i templates per la generazione del codice relativo ai

controlli utente che poi vengono assemblati a formare le diverse schermate di una applicazione per

smartwatch

android_templates: contiene i templates necessari alla generazione del modello e della

persistenza, i templates che generano i file XML relativi al layout e i templates destinati alla

generazione dei files di descrizione del progetto tra cui il file manifest.json

android_extensions: contiene le estensioni scritte in XTend e utilizzate dai templates dei

precedenti gruppi

Parti comuni

app_extensions: contiene le estensioni scritte in XTend e utilizzate da templates di entrambe le

piattaforme target

metamodel: contiene il meta–modello di riferimento

model_checks: contiene la logica che effettua controlli non strutturali (e quindi non effettuati dal

parser nel confronto con il meta–modello) scritti in linguaggio Check

Workflow

AndroidGenerator: genera tutto il codice Android

iOSGenerator: genera tutto il codice Swift

iOSAndAndroidGenerator: combinazione dei due precedenti

110

I workflow per iOS e per Android seguono lo stesso schema

Caricamento del file di input

Istanziazione del meta–modello

Lettura del modello, controllo strutturale, conversione in oggetti Java

Controllo dei vincoli non strutturali

Generazione del codice

o Generazione dei files di default, come ad esempio i file di descrizione del progetto

o Generazione dei files di default per smartwatch

o Generazione delle scene dell’applicazione per smartphone e tablet

o Generazione delle scene dell’applicazione per smartwatch

o Generazione dei files relativi al modello e alla persistenza

Conclusione

5.3.2 Design della generazione di applicazioni cross–platform

Selezione del framework

Il mercato attuale è dominato da due alternative per la produzione di applicazioni mobili cross–platform:

Xamarin e ReactNative.

Il primo prodotto è basato sul linguaggio C# per la definizione della logica applicativa e sul linguaggio XAML

per la definizione delle interfacce grafiche. XAML è dotato di binding a due vie per il collegamento tra UI e

dati e di eventi per la gestione dell’interazione con l’utente. La produzione cross–platform viene ottenuta in

modo molto diverso su iOS e su Android. Su iOS il codice sorgente viene compilato tramite ahead-of-time

compilation, la libreria .NET viene inclusa (le parti non utilizzate vengono eliminate dal linker). Su Android il

codice viene convertito in un linguaggio intermedio che viene poi viene eseguito tramite just in time

compilation sulla MonoVM, che viene eseguita in parallelo ad ART. Questi passaggi, oltre ad aumentare lo

spazio richiesto dall’app specialmente su Android, appesantiscono il processo di sviluppo: la compilazione è

lunga, il live editing impossibile. Inoltre, la limitazione maggiore di Xamarin consiste nella necessità di

definire molte parti dell’applicazione in modo autonomo per Android e per iOS a causa di divergenze nei

rispettivi sistemi operativi.

Il secondo prodotto è basato su React con JSX per il design sia delle interfacce che della logica, con la

differenza che i controlli utilizzati tramite JSX non sono controlli HTML come in React ma controlli nativi

della piattaforma su cui l’applicazione viene eseguita. L’applicazione ReactNative quindi rende viste native

orchestrate da logica definita in Javascript. Il collegamento tra le due parti viene realizzato dal framework

stesso in modo automatico. I maggiori punti di forza di ReactNative rispetto a Xamarin sono la possibilità di

avere live editing durante la scrittura del codice, una compilazione agile, una maggior qualità e reperibilità

della documentazione e la possibilità di utilizzare la maggior parte dei componenti disponibili nella libreria

di npmjs.

Per questo progetto è stato quindi scelto il framework ReactNative.

Modifiche al workflow

Dato che ReactNative non supporta le applicazioni per smartwatch, il workflow di generazione del codice

deve essere, seppur in minima parte, riadattato, anche per includere la novità di questo lavoro di tesi,

ovvero la generazione degli elementi relativi al controller

111

Caricamento del file di input

Istanziazione del meta–modello

Lettura del modello, controllo strutturale, conversione in oggetti Java

Controllo dei vincoli non strutturali

Generazione del codice

o Generazione dei files di default, come ad esempio i file di descrizione del progetto

o Generazione delle scene dell’applicazione per smartphone e tablet

o Generazione dei files relativi al controller

o Generazione dei files relativi al modello e alla persistenza

Conclusione

Architettura di destinazione

L’applicazione generata si basa su ReactNative per l’interfaccia e l’interazione con l’utente, e su Redux per

la gestione dello stato e l’interazione con la persistenza.

L’architettura di destinazione segue un pattern a livelli per favorire la soluzione di diversi problemi in

isolamento.

Figura 5.1 Architettura di riferimento per l'applicazione generata

Data layer

Si occupa della persistenza ed è implementato utilizzando librerie ibride dell’ecosistema di

ReactNative in modo da ottenere performance native ed accesso alle risorse tramite JavaScript.

Data access layer

Un layer implementato puramente in Javascript che fornisce un insieme di facades utilizzate per

astrarre i dettagli della persistenza.

State layer

Questo layer è gestito da Redux, e contiene lo stato attuale dell’applicazione. Gli oggetti sono

caricati dalla persistenza all’occorrenza e sono riscritti in caso di modifiche. L’introduzione di questo

layer presenta diversi benefici. In primo luogo, astrae dalla persistenza e presenta al layer

successivo un oggetto Javascript che contiene l’intero stato dell’applicazione. In secondo luogo,

connette il dominio di React, basato su un’implementazione dichiarativa, con il dominio delle

librerie di storage, basato su un’implementazione imperativa. Da ultimo, esso semplifica

l’interfaccia di accesso ai dati, che ora avviene tramite azioni codificabili con semplici oggetti

Javascript.

Scene model layer

Questo layer implementa l’operazione di restrizione del modello dell’applicazione al modello della

scena attualmente visualizzata. Inoltre, esso invia al layer precedente le azioni per caricare oggetti

dalla persistenza e per modificare tali oggetti. Infine, esso inietta i dati nel layer successivo

passandoli come proprietà React ai componenti che lo implementano.

112

Scene layer

Questo layer è utilizzato per aggregare diversi view controller in scene e per fornire funzionalità di

navigazione tra le diverse scene e tra i diversi view controller appartenenti alla scena

correntemente visualizzata. Inoltre, esso converte il scene model ricevuto dal layer precedente in

proprietà che verranno passate direttamente ai controlli utente, implementando così

un’operazione di decomposizione degli oggetti a singoli valori che possono poi essere utilizzati dai

componenti nativi.

View controller layer

Questo layer è utilizzato per mostrare contenuto all’utente. I controlli si collegano alle proprietà

ricevute dal layer precedente e vengono disposti sullo schermo dal solutore dei vincoli, emulando il

noto ConstraintLayout della libreria Android. I controlli utilizzati provengono dalla libreria di

ReactNative così come da librerie di terze parti. In ogni caso, essi sono resi come controlli nativi.

Solo l’orchestratore continua ad essere puramente Javascript. Questo layer è inoltre responsabile

di intercettare le azioni dell’utente e convertirle in modifiche ai dati o in azioni di navigazione da

inviare al layer precedente.

Principali sfide implementative

La struttura e le caratteristiche peculiari di ReactNative ne fanno uno strumento estremamente potente e

innovativo, ma pongono alcune limitazioni che rendono difficile la conciliazione con strumenti di sviluppo

più tradizionali, come Java o Swift. In questo capitolo verranno presentate le principali sfide incontrate in

questo lavoro di tesi, e verrà discusso il modo in cui sono state aggirate o risolte, o eventualmente il motivo

per cui sono state lasciate come “problemi aperti”, sempre con l’obiettivo di mantenere il generatore di

codice omogeneo quanto a capacità espressiva rispetto alle diverse piattaforme.

Task in background

Il tradizionale modello di concorrenza di una applicazione mobile prevede la coesistenza di un thread

principale supportato da diversi worker threads, ciascuno dedicato ad un task specifico che non è possibile

eseguire sul thread principale senza rendere la vista poco responsive. Il modello di riferimento prevede la

possibilità di modellare la necessità di tali worker threads per ciascun view controller (§3.1.7). In

ReactNative il supporto all’esecuzione di thread in background si basa sulla libreria HeadLess JS, che allo

stato attuale è supportata solo su Android. La migliore soluzione veramente cross-platform, e non basata

quindi su HeadLess JS, prevede la possibilità di avviare un solo worker thread, e presenta comunque

notevoli limitazioni. La community di sviluppatori ReactNative sta tuttavia chiedendo in modo importante

la realizzazione di un’estensione di HeadLess JS anche ai dispositivi iOS. In attesa di chiarimenti in merito al

futuro di HeadLess JS e in generale della concorrenza in ReactNative, la versione 5.0 di

MobileCodeGenerator limita il supporto per i task in background alle applicazioni native e non cross-

platform.

Layout model

I produttori di entrambi i sistemi operativi mobili maggiori (Android e iOS) stanno fortemente

incoraggiando l’utilizzo di sistemi di layout basati su vincoli, con una duplice finalità: poter modellare una

vista con una struttura piatta di controlli e ottenere un layout il più portabile e adattabile rispetto a schermi

diversi per dimensioni o risoluzione. Il modello alla base di ogni layout in ReactNative è invece basato sul

paradigma delle FlexBox, che consentono di esprimere un layout utilizzando contenitori che distribuiscono i

propri figli all’interno dello spazio ad essi destinato con strategie che ricordano le catene di controlli. Il

modello di riferimento disegnato nei precedenti lavori di tesi era fortemente basato su layout a controlli, ed

è quindi stato necessario replicare tale meccanismo nella generazione di applicazioni cross-platform. La

113

traduzione di vincoli in strutture basate su FlexBox è estremamente complessa ed inefficiente, e richiede

una completa reingegnerizzazione del layout. Per questo motivo la versione 5.0 di MobileCodeGenerator

inserisce nel codice generato una libreria di componenti dedicati ad emulare il funzionamento di un classico

ConstraintLayout. Tale libreria è stata sviluppata nel contesto di questo lavoro di tesi, ed è ritagliata sul

modello dei vincoli presentato nel capitolo §3.1.2.

Varietà dei controlli utente

Il modello di riferimento integra una vasta gamma di controlli utente che è possibile utilizzare nella

costruzione delle viste di un’applicazione. Tali componenti sono nativamente disponibili su entrambi i

sistemi operativi Android e iOS, ma non tutti appartengono alla libreria standard di ReactNative. Per questo

motivo il codice generato si appoggia a librerie proprie o di terze parti per coprire le carenze della libreria

standard di ReactNative. In particolare, il componente Card così come utilizzato nel modello di riferimento

è implementato con una libreria propria, mentre AudioPlayer, AudioRecorder, PhotocameraController,

VideocameraController e VideoView sono implementati con una libreria propria supportata da librerie di

terze parti. Infine, i controlli Spinner, Map, DatePicker, TimePicker e GridView sono ottenuti in toto da

librerie di terze parti.

Strategie di persistenza

Il modello di persistenza fornito da ReactNative è molto elementare ed è basato su un repository di valori

ciascuno associato ad una chiave, sul modello del LocalStorage fornito in ambiente web. Tale modello è

tuttavia estremamente riduttivo e semplicistico per le necessità imposte dalla traduzione del modello, ed è

quindi stato necessario appoggiarsi a librerie di terze parti al fine di integrare tutte le strategie di

persistenza previste dal modello di riferimento (§3.2)

Inizializzazione dei scene model

Il modello di riferimento permette di specificare la struttura di ciascun scene model (§3.3) necessario al

funzionamento dell’applicazione, ma non sempre permette di definire la logica necessaria alla sua

inizializzazione. Per quanto riguarda la persistenza tramite Shared Preferences, il problema è semplice: la

definizione del record da importare nel modello contiene tutte le informazioni necessarie al suo

caricamento dalla memoria persistente: la chiave. Allo stesso modo, la persistenza tramite File Storage

utilizza solo il nome del file per ottenere tutti i dati necessari. Oltre che la limitatezza delle informazioni

necessarie all’importazione dalla memoria di massa, un altro aspetto accomuna queste strategie di

persistenza: l’assenza di relazioni strutturali tra diversi record. Questo non vale per il database relazionale,

che mette proprio al centro le relazioni tra entità, e per il database cloud, la cui struttura ad albero si basa

sulla possibilità che un oggetto contenga uno o più altri oggetti ricorsivamente. Tali relazioni possono

inoltre dare luogo a grafi di dipendenza ciclici. Per questo motivo, il codice che si occupa di importare nel

scene model un’entità relazionale o un oggetto cloud non si occupa di portare in memoria anche tutte le

entità correlate, inizializzando solamente gli attributi propri (che sono peraltro sempre di tipo scalare).

Qualora sia necessario ottenere un riferimento anche ad una collezione di entità correlate ad un elemento

del scene model è necessario modellarlo come un oggetto indipendente e andare poi a modificare

conseguentemente il codice generato. La modifica richiesta comunque è minima: viene generato il codice

per il caricamento di una collezione, si tratta solo di definire il predicato utilizzato per selezionarne gli

elementi (tipicamente, utilizzando il campo identificativo di un altro elemento appartenente al scene

model). Il database relazionale è soggetto ad una ulteriore limitazione: il modello di riferimento non

consente di definire la query SQL necessaria alla selezione dell’elemento/degli elementi da importare nel

scene model. Il codice incaricato di tale importazione andrà quindi arricchito opportunamente da uno

sviluppatore. Questa limitazione non sussiste per il database cloud, in quando un’entità è completamente

114

descritta dal suo percorso rispetto alla radice, e questo può essere efficacemente modellato da un utente.

Per questo motivo il campo cloudRefPath è incluso nel modello di un AdapterBinding (§3.3.1)

Implementazione delle finestre di dialogo

L’apertura e la chiusura di una finestra di dialogo (alertDialog o progressDialog) sono tradizionalmente

implementate in modo imperativo, mentre il paradigma imposto da ReactNative è dichiarativo. Inoltre, il

modello di riferimento impone che ogni view controller abbia visibilità sulle proprie finestre di dialogo, e

non su quelle di altri view controller. Questo impone una soluzione locale alla scene per l’apertura e la

chiusura delle finestre di dialogo dei view controller componenti. Questo può essere implementato in

ReactNative sfruttando il fatto che gli eventi percorrono l’albero dei controlli dalle foglie verso la radice:

ogniqualvolta un view controller viene istanziato, esso emette un evento contenente un dizionario in cui a

ciascuna chiave corrispondono due funzioni destinate rispettivamente ad aprire e chiudere la finestra di

dialogo. La scene che ha istanziato il view controller può così mettersi in ascolto dell’evento e ottenere il

controllo delle finestre di dialogo messe a disposizione dal view controller stesso. In caso una scene abbia

più figli, ciascuno di essi emetterà un evento e la scene procederà ad aggregare i dizionari ottenuti in un

unico dizionario delle finestre disponibili. Questo consente di implementare l’apertura e la chiusura di

finestre di dialogo in modo locale alle singole scene.

Implementazione del generatore di View e Model per ReactNative

Al fine di affiancare, alla generazione di codice Java per Android e Swift per iOS, anche la generazione di

codice ReactNative per le stesse funzionalità, sono stati aggiunti al generatore i seguenti elementi

reactnative_templates: contiene tutti i templates necessari a generare Scene Layer e View

Controller Layer. In particolare, esso contiene

o Scenes.xpt: template dedicato alla generazione delle scene e della navigazione sia tra scene

che all’interno della singola scena

o ViewController.xpt: template dedicato alla generazione dei viewController (compresi i

parentViewController). Questi template si appoggiano alle funzionalità offerte dalle scene

in cui i view controller sono inseriti e richiamano i templates di generazione dei controlli

utente. Il componente che implementa il solutore dei vincoli viene inserito a questo livello.

o Screen.xpt: template dedicato alla generazione di un componente di connessione tra una

scena e un viewController in essa contenuto

o Un template per ogni tipologia di controlli: questi template seguono una struttura

consistente generando i vari elementi che servono a mostrare un controllo: stili definiti

dall’utente, vincoli di posizione e di dimensione, stato del componente, eventi da gestire e

logica di rendering

reactnative_storage_templates: contiene tutti i templates destinati a generare il Data Access

Layer. In particolare, esso contiene un template per la generazione di uno o più adapter per ogni

strategia di persistenza. Per quanto riguarda sharedPreferences, fileStorage e cloudDatabase

l’adapter generato è soltanto uno (per tipologia), mentre per relationalDatabase viene generato un

adapter per ogni entità più un adapter globale che gestisce la connessione al database.

reactnative_project_structure: contiene tutti i templates destinati a generare file di struttura del

progetto e files statici, che costituiscono una libreria di riferimento per gli altri componenti. I files

che descrivono il progetto sono sempre generati, mentre i files di libreria vengono generati

solamente all’occorrenza.

o AndroidManifest.xpt: genera il file manifest.json necessario ad eseguire l’applicazione su

Android

115

o app_json: genera il file app.json che contiene metadati dell’applicazione utilizzati dal

runtime di ReactNative

o build_gradle.xpt: genera il file build.gradle necessario per la compilazione dell’applicazione

e l’inclusione delle dipendenze su Android

o index.xpt: genera il file index.js, entry point dell’applicazione ReactNative

o package_json.xpt: genera il file package.json che indica le dipendenze dell’applicazione da

installare tramite Yarn o NPM. Le dipendenze sono generate in base alle feature

effettivamente richieste dall’applicazione, in modo da ridurre la dimensione del bundle.

o settings_gradle.xpt: genera il file settings.gradle contente le impostazioni di compilazione

per Android

o Audio.xpt: genera gli strumenti propedeutici all’accesso a registrazione e riproduzione

audio

o CameraView.xpt: genera un componente capace di utilizzare la fotocamera del device per

scattare foto o registrare video. Questo componente viene inserito nell’applicazione

quando sono previste funzionalità di interazione con la fotocamera.

o Card.xpt: genera un componente capace di produrre un layout a card come definito da

Google Material UI.

o ConstraintLayout.xpt: genera il componente che implementa il ConstraintLayout e la

libreria di risoluzione dei constraints.

o ContentValues.xpt: genera una libreria di appoggio utile nell’interazione con il database al

momento dell’inserimento o della modifica di un’entità

o DatabaseStore.xpt: genera una libreria di astrazione sul database che mostra un generico

repository al posto di una tabella con i suoi record.

o VideoPlayer.xpt: genera un componente capace di riprodurre un video con i controlli

elementari per avviare o arrestare la riproduzione

Implementazione della generazione del controller

La generazione del controller è stata aggiunta nella versione 5.0 di MobileCodeGenerator solamente per la

generazione di codice ReactNative. Essa ha richiesto diverse modifiche e aggiunte al codice di

MobileCodeGenerator.

Le modifiche più importanti riguardano il meta–modello. È stato infatti necessario definire nuove strutture

per codificare le modifiche apportate nel modello di riferimento con l’introduzione di AdapterBinding e

ModelConnector.

Per implementare nell’applicazione generata lo State Layer è stato aggiunto un nuovo set di templates

chiamato reactnative_controller. I templates di tale gruppo si occupano della generazione delle azioni

necessarie ad operare con la persistenza e dei reducers in grado di eseguirle. La più grande sfida risolta da

questo layer riguarda l’asincronia delle operazioni con la persistenza: qualsiasi interazione, sia essa di

lettura o scrittura, richiede il disaccoppiamento temporale di richiesta e risposta. Per gestire questo fatto

ogni operazione è in realtà codificata da due azioni, che ne segnano l’inizio e la conclusione e consentono ai

reducer di operare in modo sincrono anche in presenza di operazioni asincrone: i reducer operano in modo

sincronizzato con inizio e fine dell’operazione asincrona. Tali azioni sono riconoscibili nel codice dai suffissi

_REQUEST e _COMPLETE applicati al tipo dell’azione. Tali azioni tuttavia sono opache al livello superiore

grazie al middleware redux-thunk per redux. A fianco della generazione di tali azioni vengono infatti

generati anche alcuni thunks necessari ad orchestrare l’invio delle azioni al reducer e l’operazione

116

asincrona stessa. Il livello superiore invia al runtime redux non una semplice azione, ma un thunk, in modo

da incapsulare completamente la gestione della comunicazione asincrona.

Figura 5.2 Utilizzo di un thunk nelle interazioni con la persistenza

Per implementare il Scene Model Layer è stata introdotta la generazione di componenti connessi allo stato

di redux che agiscono da incapsulatori per le scene. Tali componenti interagiscono con lo stato in modo da

caricare dalla persistenza gli elementi necessari a comporre il scene model. Quando tutti gli elementi sono

disponibili, ovvero quando la persistenza termina le operazioni asincrone, tali componenti eseguono

l’operazione di riduzione dello stato al scene model estraendo gli elementi di loro interesse e inoculandoli

nei componenti che implementano layers più vicini all’utente.

A livello di Scene Layer è stato necessario modificare la generazione degli screen. Tali componenti

costituiscono infatti il luogo naturale in cui implementare i Model Connectors. L’implementazione

comunque non richiede modifiche sostanziali se non l’aggiunta di qualche proprietà passata al view

controller che ciascuno di essi incapsula, in modo che le proprietà calcolate dai connettori raggiungano i

controlli finali.

A livello di View Controller Layer sono state modificate le proprietà dei componenti, in modo che possano

assumere un valore passato dai livelli inferiori o un valore di default (il valore che assumevano prima delle

modifiche descritte in questo capitolo).

5.3.3 Generazione del codice

In questa sezione vengono presentate le regole codificate nei templates del generatore di codice per varie

categorie di elementi, in modo da fornire una panoramica su come le considerazioni esposte nel capitolo

§5.3.2 siano state inserite nell’applicazione generata.

117

Generazione dei controlli utente

Ogni categoria di controlli utente definita nel modello di riferimento è associata ad una classe che definisce

un appropriato componente React. Ne consegue che per ogni definizione di un controllo utente nel

modello dato in input a MobileCodeGenerator viene generata un’istanza della corrispondente classe di

componenti, le cui proprietà vengono inizializzate a partire dalla definizione stessa del controllo utente.

Dato che React è fortemente basato sulla composizione, la relazione di ereditarietà tra UiPhoneControl e le

sue sottoclassi viene resa con una relazione di composizione: il componente caratteristico per la classe di

controlli viene inserito all’interno di un altro componente (generico rispetto alla tipologia di controlli) che si

occupa di tutti gli aspetti comuni tra le diverse tipologie di controlli. Questa relazione permette al

componente esterno di accedere alle proprietà di istanza, leggere quelle di suo interesse, ed inoltrare tutte

le altre alla classe caratteristica. Infine, per migliorare la leggibilità del codice, viene generato per ogni

istanza un oggetto contenente tutte le proprietà stilistiche, come margini, padding, colori e dimensioni del

testo. Tale oggetto viene poi passato all’istanza corrispondente tramite proprietà, la sua definizione non

inline serve solo a migliorare la leggibilità del codice.

A titolo di esempio di quanto esposto, viene presentata l’espansione di un componente di tipo Label, la cui

classe caratteristica è la classe Text che, una volta composta con la classe generica, assume il nome CText.

Tal componente ha le seguenti caratteristiche

un margine di 10 punti e un padding di 5 punti su tutti i lati

una larghezza fissa al 50% del padre e un’altezza fissa a 40 punti

un vincolo di posizione che allinea il lato superiore al bordo superiore del contenitore

un vincolo di posizione che allinea il lato sinistro al bordo destro di un altro elemento

testo centrato nello spazio disponibile

font in variante grassetto e di colore rosso alto 30 punti

<CText

id=”Id123456”

style={styles.eId123456}

marginTop={10}

marginStart={10}

marginEnd={10}

marginBottom={10}

defaultHeight={25}

defaultWidth={80}

posX={0}

posY={0}

constraintWidthPercent={0.50}

constraintHeight={40}

constraintTop={{ ref: null, side: ‘top’ }}

constraintStart={{ ref: ‘eId987654’, side: ‘end’ }}

>

{this.props.eId123456Title !== undefined ? this.props.eId123456Title : “Un contenuto statico”}

</CText>

// Nella collezione degli stili (che si trova nel file che implementa il viewController a cui la label appartiene) troviamo

{

// Altri stili…

eId123456: {

paddingTop: 5,

paddingBottom: 5,

paddingStart: 5,

paddingEnd: 5,

textAlign: ‘center’,

fontWeight: ‘bold’,

fontSize: 30,

color: ‘#FF0000’

}

}

118

Le proprietà in arancio sono gestite dalla classe generica e non vengono inoltrate alla classe Text, le

proprietà colorate in verde sono gestite direttamente dalla classe Text e le proprietà in bianco sono

trasversali rispetto alla tipologia di controllo utente e sono gestite direttamente dal framework. È evidente

come le proprietà gestite dalla classe generica corrispondano a proprietà definite per UiPhoneControl,

mentre le proprietà in verde corrispondano a proprietà definite sulla sottoclasse in questione. Questo

meccanismo è utilizzato per la definizione delle classi corrispondenti a qualsiasi controllo utente.

Nell’esempio sono assenti le control chains, che vengono comunque inserite come proprietà passate a

CText e gestite dalla classe generica in quanto parte del meccanismo di posizionamento. Nel caso in cui la

label di cui sopra avesse fatto parte di una catena di controlli di tipo weighted (cosa non possibile

nell’esempio in quanto il controllo è soggetto a vincoli sia per l’asse verticale che per quello orizzontale), la

definizione avrebbe portato i seguenti attributi

<CText

{/* Altri attributi */}

constraintChain=‘weighted’

constraintChainAxis=‘horizontal’,

constraintWeight={3}

constraintChainPrev=’eId135790’

>

{this.props.eId123456Title !== undefined ? this.props.eId123456Title : “Un contenuto statico”}

</CText>

Generazione dei view controller

Per ogni view controller definito nel modello di riferimento viene creata una classe di componenti, che sarà

poi istanziata al momento dell’inserimento del view controller stesso come figlio di una scena. Tale classe

ricopre due ruoli essenziali: contiene le definizioni di tutti i controlli inseriti nel view controller, occupandosi

quindi di istanziare il ConstraintLayout necessario per le operazioni di rendering, e agisce come proxy per

tutti gli eventi che provengono dai controlli utente e devono essere inoltrati alla scena. La gestione degli

eventi infatti difficilmente avviene localmente ad un view controller in quanto ogni view controller può

essere inserito in più di una scena, e in ciascuna di esse può operare in modo diverso. In altri termini, la

logica dell’applicazione è definita a livello di scena, e non di view controller. Ogni classe rappresentante un

view controller è inoltre corredata da una struttura dati accessoria, organizzata come un dizionario. Tale

struttura dati ha il compito di contenere la definizione di tutti gli stili applicati al view controller stesso

(come il colore di sfondo) e a tutti i controlli in esso contenuti. Gli stili sono indicizzati in base

all’identificativo univoco dei componenti a cui sono associati. Da ultimo, vi è un ulteriore compito

accessorio svolto dalla classe che rappresenta un view controller, ed è inoltrare alla scena in cui è inserito le

funzioni necessarie ad utilizzare le finestre di dialogo definite nel view controller stesso. Nel caso in cui si

tratti di un parent view controller, esso inoltra alla scena un dizionario contenente le definizioni delle

finestre di dialogo di tutti i child view controller in esso contenuti.

A titolo di esempio, presentiamo le parti più rilevanti di un semplice view controller che modella una

funzionalità di registrazione. Esso contiene tre componenti: due caselle di testo (CTextInput) e un pulsante

di conferma (CTouchableHighlight).

// Dizionario degli stili const styles = StyleSheet.create({ ejr7ufryu: { width: '100%', height: '100%', backgroundColor: '#ffffff' }, ejr7ugetx: { paddingTop: 0, paddingBottom: 0, paddingStart: 0,

119

paddingEnd: 0, fontSize: 16, }, ejr7uij59: { paddingTop: 0, paddingBottom: 0, paddingStart: 0, paddingEnd: 0, fontSize: 16, }, ejr7uk18g: { paddingTop: 0, paddingBottom: 0, paddingStart: 0, paddingEnd: 0, borderRadius: 10, backgroundColor: '#cecece', }, ejr7uk18g_wrapper: { flex: 1, justifyContent: 'center', alignItems: 'center' }, ejr7uk18g_inner: { textAlign: 'center', color: '#000000', }, }); // Classe che definisce il componente export default class Component_Registration extends React.Component { registerInterface(componentInterface) { // Metodo utilizzato per registrare gli AlertDialogs e i ProgressDialogs esposti da questo componente // O da uno dei figli, qualora questo sia un parent view controller // Quando tutti i figli si sono registrati, questo metodo richiama una funzione passata al componente // dalla scena per fornire le chiamate di apertura delle finestre di dialogo } // Proxy per gli eventi provenienti dai controlli // Ogni evento possiede due proxy, uno specifico e uno generico // - quello specifico viene eseguito solo per eventi provenienti dal componente // - quello generico viene eseguito per eventi provenienti da tutti i componenti della stessa tipologia // Ad esempio, un proxy generico è onTextChange, che reagisce a tutti gli eventi di modifica di tutte le textbox // mentre un proxy specifico è onE123456Change, che reagisce solo alle modifiche apportate alla textbox con id e123456 onjr7ugetxChange(text) { if (this.props.onEjr7ugetxValueChange) this.props.onEjr7ugetxValueChange(text); if (this.props.onTextChange) this.props.onTextChange('jr7ugetx', text); } onjr7uij59Change(text) { if (this.props.onEjr7uij59ValueChange) this.props.onEjr7uij59ValueChange(text); if (this.props.onTextChange) this.props.onTextChange('jr7uij59', text); } onjr7uk18gPress() { if (this.props.onEjr7uk18gPress) this.props.onEjr7uk18gPress(); if (this.props.onButtonPress) this.props.onButtonPress('jr7uk18g'); } // Metodo predefinito di ReactNative // Si occupa di definire come questo componente appare render() { const { style, onInterface, ...passProps } = this.props; return ( <> <ConstraintLayout style={styles.ejr7ufryu}> // Istanziazione del constraint layout // Tutti gli elementi contenuti nel constraint layout sono controlli utente // che vengono inseriti nel vista. Sono riportati in questo esempio solo gli attributi // relativi ai vincoli di posizione e dimensione e alla gestione degli eventi <CTextInput id="ejr7ugetx" marginTop={170} marginStart={30} marginEnd={30} constraintStart={{ ref: null, side: 'start' }} constraintEnd={{ ref: null, side: 'end' }} constraintTop={{ ref: null, side: 'top' }} style={styles.ejr7ugetx} onChangeText={this.onjr7ugetxChange} value={this.props.ejr7ugetxValue} placeholder="Name">

120

</CTextInput> <CTextInput id="ejr7uij59" marginTop={40} constraintStart={{ ref: 'ejr7ugetx', side: 'start' }} constraintEnd={{ ref: 'ejr7ugetx', side: 'end' }} constraintTop={{ ref: 'ejr7ugetx', side: 'bottom' }} style={styles.ejr7uij59} onChangeText={this.onjr7uij59Change} value={this.props.ejr7uij59Value} placeholder="Surname"> </CTextInput> <CTouchableHighlight id="ejr7uk18g" marginTop={60} constraintStart={{ ref: 'ejr7ugetx', side: 'start' }} constraintEnd={{ ref: 'ejr7ugetx', side: 'end' }} constraintTop={{ ref: 'ejr7uij59', side: 'bottom' }} onPress={this.onjr7uk18gPress} style={styles.ejr7uk18g} activeOpacity={1} underlayColor={'#cecece'} > <View style={styles.ejr7uk18g_wrapper}> <Text style={styles.ejr7uk18g_inner}>OK, let's go!</Text> </View> </CTouchableHighlight> </ConstraintLayout> </> ); } }

È importante notare che i controlli utente non hanno un valore definito all’interno del view controller, ma

che i valori sono acquisiti dalle proprietà: questo permette alla scena che istanzia il view controller di

fornire i valori per i controlli prendendoli dal scene model e personalizzando così il comportamento del view

controller.

La seguente figura mostra il risultato dell’esempio precedente

Figura 5.3 Esempio di vincoli in un ViewController

121

Generazione delle scene

La generazione delle scene è la parte più complessa del generatore, in quanto vi sono diverse logiche di

aggregazione dei view controller componenti, e le differenze tra di esse sono piuttosto profonde. Inoltre,

ciascuna scena viene definita due volte: la prima definizione viene utilizzata se l’applicazione è eseguita su

smartphone, la seconda in caso il device di esecuzione sia un tablet. Al fine di ridurre la complessità

mediante divisione in sottoproblemi, il generatore crea diversi componenti per ogni scena. Nel seguito di

questo paragrafo il termine scena denota il caso specifico della definizione di una scena per uno specifico

device: ogni scena del modello di riferimento corrisponde naturalmente a due scene specifiche ai device, in

base a quanto detto sopra.

Procedendo con un approccio bottom-up, il primo componente generato è il parent view controller, se

richiesto. In particolare, il parent view controller viene generato solo per scene la cui tipologia è MULTI_VC,

in quanto questo è l’unico caso in cui sia necessario un componente per definire la logica di disposizione dei

figli sullo schermo. In tutti gli altri casi, viene mostrato un unico view controller a tutto schermo.

Il secondo componente definito è chiamato Screen e serve a dare uniformità ai figli diretti di una scena

rispetto al componente successivo: ogni videata, sia essa un singolo view controller o un gruppo di view

controller incapsulati in un parent view controller, viene a sua volta inclusa in uno Screen. Il ruolo

fondamentale dello Screen è quello di applicare correttamente i Model Connectors validi per la scena per

cui sono definiti: ciascuno Screen ha accesso al scene model e conosce l’identità dei controlli utente inseriti

nei suoi figli, e può quindi applicare le logiche di conversione dati definite dai Model Connectors. In caso di

interazioni bidirezionali, è sempre compito dello Screen applicare le modifiche ottenute attraverso un

Model Connector al Scene Model: gli oggetti del Scene Model sono immutabili, e quindi ogni modifica

richiede una reistanziazione del frammento modificato (ogni adapter binding infatti è indipendente dagli

altri), che deve essere poi inviato alla persistenza attraverso un componente più in alto nella gerarchia.

Il terzo componente generato ha il ruolo essenziale di navigatore, in quanto permette la navigazione tra i

diversi screen facenti parte della scena. Questo componente ha il compito di fornire ai livelli più bassi le

primitive per effettuare azioni di navigazione imperativa all’interno della scena e di gestire l’eventuale barra

delle tabs (qualora la scena abbia tipologia SINGLE_VC_TAB). La navigazione tra scene diverse è

implementata sempre con un componente di questo tipo, ma definito globalmente nella radice

dell’applicazione. Il navigatore è generato con il supporto di una libreria esterna.

Il quarto e ultimo componente richiesto per il corretto funzionamento di una scena, chiamato scene model

master, è il componente responsabile del scene model. Tale componente non appartiene come tutti gli altri

al scene layer, ma al scene model layer. Ciononostante, è necessario definire questo componente per ogni

scena, in modo da mantenere l’indipendenza della scena stessa. Quando una scena viene visualizzata,

questo componente viene inserito nella vista e, sebbene non abbia alcun riscontro grafico visibile, esso

richiede allo state layer i dati necessari a formare il scene model. Quando il scene model è pronto, il

navigatore viene caricato sullo schermo. Questo viene fatto per evitare inconsistenze nei dati che poi

vengono inoltrati ai controlli utente. Ogni volta che si verificano modifiche a qualche dato, lo screen di

competenza inoltra la nuova versione dei dati a questo componente, che la persiste nello state layer. La

sincronizzazione tra state layer e memoria di massa viene gestita in altri livelli, e non è di competenza di

questo componente.

Il seguente diagramma riassume la struttura logica dei componenti generati per ogni scena, nel caso in cui

la scena sia di tipo SINGLE_VC.

122

Figura 5.4 Struttura di una scena di tipo SINGLE_VC

Il seguente diagramma riassume la struttura logica dei componenti generati per ogni scena, nel caso in cui

la scena sia di tipo SINGLE_VC_TAB.

Figura 5.5 Struttura di una scena di tipo SINGLE_VC_TAB

Il seguente diagramma riassume la struttura logica dei componenti generati per ogni scena, nel caso in cui

la scena sia di tipo MULTI_VC.

Figura 5.6 Struttura di una scena di tipo SINGLE_VC

Generazione delle definizioni del modello

Durante l’esecuzione dell’applicazione, è necessario che tutti i dati in uso da parte della vista corrente siano

memorizzati nello state layer, in modo da essere immediatamente accessibili quando la vista viene

mostrata. Il caricamento dei dati dalla memoria persistente avviene su richiesta da parte dei scene model

master, che deve quindi avere conoscenza della struttura del modello per poterlo interrogare nel momento

123

in cui vengono calcolati i valori associati agli adapter bindings. In questa sezione viene presentato come gli

elementi definiti per ogni strategia di persistenza vengono definiti nel codice generato per l’applicazione.

In merito alle Shared Preferences, il codice da generare è molto ridotto e consiste essenzialmente in una

sola classe, che agisce da proxy verso il servizio nativo di memorizzazione. Il servizio nativo è infatti

mancante di un elemento contenuto nel modello di riferimento: un valore predefinito per una data chiave.

Il proxy quindi ha il ruolo fondamentale di restituire al chiamante il valore di default nel caso in cui una

chiave non esista nella memoria di massa.

In merito alla memorizzazione tramite files, vengono generati due elementi: una classe che agisce come

proxy verso le API esposte da una libreria di terze parti, che a sua volta utilizza l’interfaccia del file system

nativo, e una tabella dei files disponibili, contenente tutte le informazioni necessarie per accedere ad un

dato file. Questo semplifica l’accesso ai files, che può avvenire semplicemente a partire dal loro nome, che

è utilizzato come chiave nell’indicizzazione della tabella dei files disponibili. Il proxy supporta tre operazioni

fondamentali: la lettura dell’intero contenuto di un file, la sovrascrittura dell’intero contenuto di un file e

l’eliminazione di un file. Ciascuna di esse richiede come parametro una riga della tabella dei files.

In merito alla memorizzazione tramite database relazionale, viene generate due classe per ogni entità, più

una classe che svolge funzioni di interazione con la persistenza. Delle due classi generate, la prima contiene

la definizione della tabella in cui i dati sono memorizzati e una serie di metodi di comodo per ottenere

istanze dell’entità dalla memoria persistente in base ad un predicato. La seconda classe svolge un ruolo di

meta-classe, in quanto contiene informazioni in merito ai valori predefiniti dei vari attributi al momento

della creazione di una nuova istanza dell’entità, piuttosto che la logica utilizzata per convertire un’entità in

una query SQL di aggiornamento del database (logica necessaria per la persistenza delle modifiche). Non

esiste alcuna classe che modelli l’entità: questo è necessario in quanto gli oggetti gestiti dallo state layer, in

base alla documentazione di Redux, devono essere oggetti JavaScript puri, senza ereditarietà prototipica. Il

proxy ha cura di mantenere la connessione con il database, inizializzare le tabelle al primo avvio

dell’applicazione e inviare al database stesso le query generate dalla logica applicativa.

In merito infine alla memorizzazione tramite database cloud, viene generata solo una classe generale, che

fornisce funzioni di interfaccia con la libreria (di terze parti) che gestisce la connessione con il database vero

e proprio. Infatti, il database cloud memorizza gli oggetti come un albero JSON, il cui contenuto è

nativamente utilizzabile in JavaScript. Per i motivi già esposti parlando del database relazionale, non è

possibile definire una classe per ogni categoria di oggetti memorizzati nel database cloud. Gli oggetti

ottenuti dal database vengono utilizzati così come sono, ovvero come puri oggetti JavaScript. L’accesso al

database cloud avviene in modo simile all’accesso alle default preferences, ovvero utilizzando una chiave

che, in questo caso, è il path del sottoalbero che la logica applicativa chiede di importare.

Dato che tutte le interazioni con la persistenza, sia essa di massa o in cloud, richiedono operazioni

asincrone, lo state layer fornisce un meccanismo di cache delle risposte: quando la logica applicativa

richiede dati ad una strategia di persistenza, viene dapprima controllata la cache locale. In caso di cache hit,

i valori vengono ritornati in modo sincrono, mentre in caso di cache miss viene effettuata una richiesta

asincrona alla memoria di massa o al cloud. Quando la memoria risponde con i dati, la cache viene

aggiornata e i dati vengono ritornati al chiamante. I dati copiati nella cache sono chiamati copia locale,

mentre quelli memorizzanti in memoria di massa o nel cloud sono chiamati copia persistente. Nel momento

in cui la logica applicativa prevede la modifica di qualche dato, la copia locale viene aggiornata in modo

sincrono, in modo da ottenere una user experience più fluida. Una volta aggiornata la copia locale, si

124

procede all’aggiornamento asincrono della copia persistente, in modo da mantenere la coerenza della

cache con il modello persistito. In caso di cancellazione di dati invece l’ordine delle operazioni è simile alle

operazioni di lettura: prima viene eliminata la copia persistente, e, una volta completata l’operazione

asincrona, viene eliminata la copia locale. Questo evita che vi siano glitch a livello di interfaccia utente o di

interazione tra state layer e scene model layer.

125

6 Valutazione Questa sezione contiene una valutazione dell’efficacia dell’approccio implementato dalla toolchain formata

da Protocode e MobileCodeGenerator alla generazione di applicazioni mobili cross-platform. Per questa

valutazione verrà prodotta una semplice applicazione effettuando alcune misurazioni sia a livello di

processo sia a livello di prodotto. In particolare, verranno esaminati la distribuzione del tempo necessario a

generare l’applicazione rispetto ai vari task necessari, la percentuale di codice generato rispetto al codice

completo, la qualità del codice generato e le principali limitazioni di questo approccio.

Pur essendo l’applicazione di test molto semplice, essa consente di esercitare le principali funzionalità della

toolchain e di evidenziarne i principali limiti in particolare per quanto riguarda la componente controller,

che costituisce la maggiore innovazione di questo lavoro di tesi.

Al fine di eseguire valutazioni il più possibile realistiche, il caso di test è stato selezionato considerando

alcune funzioni di un’applicazione precedentemente realizzata per un caso reale. In questo modo è

possibile evitare aberrazioni nei risultati che deriverebbero dall’utilizzo di una applicazione progettata ad

hoc per questo tipo di valutazioni.

Non verranno effettuati confronti tra l’applicazione ReactNative prodotta in questo test e le corrispondenti

applicazioni native in termini di dimensione della codebase in quanto le differenze tra i vari linguaggi

renderebbero tale confronto poco significativo.

6.1 Presentazione del caso di test Per questa valutazione andremo a produrre un’applicazione che assolva le funzioni di un libretto

universitario elettronico. Lo studente che utilizza l’applicazione può inserire diverse carriere universitarie

(una per ciascun titolo di studio conseguito più quella corrente), e per ciascuna carriera può registrare gli

esami che deve sostenere e gli esami che ha sostenuto con i rispettivi voti e pesi in CFU. Da ultimo, può

consultare alcune statistiche relative a ciascuna carriera, quali la media ponderata delle valutazioni, la base

di laurea, il numero di CFU non ancora acquisiti e le proiezioni della sua valutazione se, per i restanti CFU,

prendesse sempre 18 o sempre 30.

La schermata iniziale dell’applicazione simula un login, e chiede semplicemente all’utente di inserire nome

e cognome. Su tali campi è attiva una connessione con il model, che salva i dati inseriti come

sharedPreferences.

126

La prima schermata autenticata visualizzata permette all’utente di selezionare una carriera tra quelle in

corso, o di passare alla schermata per l’aggiunta di una nuova carriera. Gli elementi della lista sono generati

a partire dal model, nella fattispecie dalle entità rappresentanti carriere salvate nel database relazionale

locale. Nella schermata di creazione di una nuova carriera non vi è alcuna connessione con il model in

quanto la carriera in oggetto non è ancora stata inserita nel model stesso. Tale schermata richiede quindi la

scrittura manuale del codice necessario ad espletare la funzione di inserimento di una carriera nel

database. Chiaramente, tale codice si avvantaggia di azioni e reducer già generati allo scopo.

Selezionando una carriera si accede alla scena di dettaglio, che presenta un layout a schede. Una prima

scheda mostra le statistiche sopra citate, una seconda scheda mostra gli esami registrati in quella carriera

(siano essi sostenuti o no) e una terza schermata mostra le date di inizio e fine carriera.

La schermata relativa alle statistiche presenta alcune connessioni con il model, che però vanno rifinite

manualmente. Ad esempio la casella di testo che mostra la media è connessa all’intera lista di esami

registrati nella carriera, ma è necessario scrivere a mano il codice per ridurre tale lista ad un numero.

Questo vale in generale per il calcolo di qualsiasi aggregato presente in questa videata.

La schermata relativa alle informazioni sulla carriera presenta due connessioni con il model, una per

ciascuna data mostrata. Se la carriera è in corso, la data finale viene omessa e viene mostrato il pulsante

per terminare la carriera. La logica di funzionamento di tale pulsante non è generabile automaticamente.

127

La schermata relativa agli esami ha una connessione tra gli elementi della lista e il model: ogni cella è

generata a partire da un oggetto che rappresenta un esame nel database relazionale. Selezionando una

cella si accede alla schermata di dettaglio dell’esame, che mostra il titolo e l’eventuale voto o dà la

possibilità di inserire il voto stesso. Di nuovo, la logica di inserimento del voto richiede un intervento del

programmatore, ma può avvantaggiarsi per gran parte di codice automaticamente generato.

Da ultimo, è presente una schermata per la registrazione di un nuovo esame. Tale schermata è analoga a

quella per l’inserimento di una nuova carriera, e presenta quindi gli stessi problemi.

6.2 Produzione dell’applicazione A seguito della progettazione dell’applicazione in termini di struttura dell’interfaccia utente, modello di

persistenza dei dati e definizione della navigazione, è stato possibile procedere all’attività di

prototipizzazione vera e propria. Tale attività è stata attentamente cronometrata in modo da identificare i

task di cui essa è composta e la durata relativa di ciascuno di essi, in modo da poter evidenziare quali parti

sono più critiche di altre. Sono stati prototipizzati, nell’ordine, il model, la view e il controller. Da tale

conteggio sono esclusi la protipizzazione del manifest in quanto di durata trascurabile e la prototipizzazione

dell’applicazione per smartwatch in quando non è possibile generare applicazioni per smartwatch

utilizzando ReactNative, e quindi sarebbe stata un’attività poco significativa per il seguito dell’analisi.

128

Al termine dell’attività di prototipizzazione è stato scaricato il file XMI contenente il modello strutturato.

Tale file è stato utilizzato come input per MobileCodeGenerator in modo da produrre il codice sorgente

vero e proprio. A questo punto è stato effettuata una prima valutazione della dimensione della codebase

relativa a ciascun layer dell’applicazione ottenuta. Tale conteggio è stato omesso dall’analisi temporale. Lo

strumento utilizzato per il conteggio è cloc.

Il passaggio successivo è stato andare a completare le parti dell’applicazione che non è stato possibile

modellare in Protocode o generare automaticamente con MobileCodeGenerator. Tali parti riguardano il

passaggio di parametri durante la navigazione, la gestione delle scene inerenti l’inserimento di nuove entità

nel database, la computazione degli aggregati, la gestione del rendering condizionale di alcuni componenti.

Alla fine, l’applicazione è stata installata sul device e verificata nel suo funzionamento. Si è poi completa

l’analisi con un conteggio finale della dimensione della codebase relativa a ciascun layer.

Il processo è stato eseguito su un Asus F556UJ equipaggiato con processore Intel i5 di 6a generazione

@2.30GHz e 12GB RAM, sistema operativo Windows X.

Nell’analisi del processo va considerata l’esperienza nell’utilizzo di Protocode maturata durante il suo

sviluppo, che per certo riduce parzialmente i tempi di prototipizzazione.

6.3 Analisi dell’applicazione generata

6.3.1 Analisi del tempo impiegato

Figura 6.1 Analisi del tempo impiegato per lo sviluppo dell'app di valutazione

Questo diagramma mostra la distribuzione del tempo impiegato allocata ai vari task svolti. L’attività

preponderante da un punto di vista temporale è la prototipizzazione dell’applicazione, con un tempo

relativo del 44.59%, sottostimato a causa dell’esperienza acquisita nell’uso di Protocode durante lo

sviluppo. La seconda attività per importanza è il completamento manuale dell’applicazione, che richiede il

40.54% del tempo. Tale numero verrà giustificato in seguito dall’analisi qualitativa dell’applicazione

129

generata. L’efficienza di MobileCodeGenerator è dimostrata dall’ 1.35% richiesto per la sua attività. Il resto

del tempo è speso in attività automatiche di installazione di dipendenze, compilazione e installazione. La

durata di tali attività tuttavia è indipendente dall’approccio scelto per la produzione dell’applicazione, ed è

quindi poco significativa da analizzare.

6.3.2 Analisi quantitativa della generazione del codice

La seguente tabella mostra la dimensione della codebase relativa a ciascun layer dell’applicazione prodotta,

calcolata prima e dopo le modifiche effettuate manualmente.

Generated Final

Data access layer 291 291 State layer 1795 1795 Scene model and scene layers 1949 2024 View controller layer 1897 1973 TOTAL 5932 6083

Tabella 6.1 Dimensione della codebase dell'app di valutazione

In questo conteggio non è presente il Data Layer in quanto implementato con librerie native la cui

dimensione è invariante rispetto a questa analisi, e sono stati fusi Scene Model Layer e Scene Layer in

quanto intaccati, in questa analisi, per gli stessi motivi.

Un primo approccio di analisi black box rileva una differenza, nella codebase totale, di sole 150 righe

aggiuntive, pari a circa il 2.4% e imputabili solamente a modifiche relative ai layer più alti nella gerarchia.

Un secondo livello di analisi, più preciso, rileva grazie ad un algoritmo di calcolo delle differenze, modifiche

per un totale di circa 250 righe, pari al 4% del totale.

Si nota immediatamente che il Data Access Layer e lo State Layer non hanno subito modifiche. Se per Data

Access Layer questo è comprensibile data la definizione precisa e ristretta del ruolo di tale layer, per lo

State Layer non è scontato, ed è possibile solamente grazie al fatto che l’applicazione di test non si

interfaccia con alcuna API esterna ad essa (non è considerata API esterna l’interfaccia con il database

cloud). Qualora vi fosse la necessità di introdurre una interazione con un application server, tale interazione

andrebbe inserita a livello di State Layer.

La percentuale di codice generata è molto elevata, e ciò è possibile anche grazie alla semplicità

dell’applicazione di valutazione (si veda il capitolo relativo alle limitazioni). Tuttavia, è possibile concludere

che anche aggiungendo funzionalità critiche per la toolchain considerata, la percentuale di codice resta

elevata, superiore al 90%. Questo dimostra l’efficacia della toolchain presentata in questo lavoro di tesi.

6.3.3 Analisi qualitativa del codice generato

I template su cui si basa la generazione del codice sono stati redatti a partire dalle indicazioni rilasciate dai

produttori delle librerie su cui esso si appoggia, in primis Redux, React e ReactNative, e dall’esperienza della

community online (principali fonti sono il blog di Dan Abramov, il creatore di Redux, e stackoverflow.com, la

più grande community di sviluppatori online). Dal punto di vista della strutturazione la qualità del codice è

molto elevata. La problematica maggiore dal punto di vista della qualità del codice generato risiede nello

scarso contenuto semantico degli identificativi. Per evitare problemi dovuti a conflitti di nome, i nomi

utilizzati nel codice sono sempre creati a partire dall’identificativo univoco e casuale del controllo a cui si

riferiscono, a cui viene concatenato un prefisso o un suffisso a seconda dei casi. Questo fa sì che sia chiaro il

ruolo di una proprietà, ma non il controllo a cui essa è riferita. Nel caso in cui un ruolo sia valido per più

elementi di una interfaccia, la semantica povera dei nomi rende difficile modificare il codice manualmente

130

dopo la generazione. Ciò spiega, almeno in parte, la quantità di tempo necessaria alla modifica manuale del

codice. Di contro, i moderni ambienti di sviluppo offrono quasi sempre funzionalità di rename intelligente,

quindi non è difficile, per un programmatore, modificare gli identificativi automatici con nomi dal maggior

contenuto semantico. Questo problema quindi si presenta solo nel caso di view controller particolarmente

ricchi di controlli, e anche in tale caso è facilmente risolvibile grazie ai moderni IDE.

6.3.4 Limitazioni

La versione attuale di Protocode e MobileCodeGenerator presenta diverse limitazioni, alcune delle quali

potrebbero essere colmate in futuri lavori di tesi. In questa sezione vengono riportate le principali:

Calcolo di aggregati

Il calcolo di valori aggregati a partire da una lista di oggetti, siano essi oggetti cloud, entità o righe di

un file, non può essere modellato in Protocode. È possibile estendere l’interfaccia di definizione dei

ModelConnectors in modo da generare automaticamente alcuni aggregati. Tuttavia, tale approccio

sarebbe comunque limitante rispetto alle potenzialità di un codice scritto direttamente, e un design

grafico degli algoritmi finirebbe per essere poco pratico e poco conveniente da un punto di vista

temporale

Selezione di oggetti nei modelli

La versione attuale di Protocode permette di definire la forma di un scene model, ma non le

specifiche istruzioni per costruirlo. Non è quindi possibile specificare l’identificativo dell’entità di

interesse, o un criterio di filtro per la creazione di una lista di entità. Tali criteri vanno specificati

modificando apposite righe nel codice generato da MobileCodeGenerator. Tali righe sono

contrassegnate da un apposito commento nel codice generato al fine di favorire la loro

identificazione. È teoricamente possibile estendere Protocode e MobileCodeGenerator per

implementare i casi più semplici di tali predicati di selezione, per quanto la potenza espressiva di

tale implementazione sarebbe senza dubbio inferiore alla libertà della specifica, via codice, di tali

predicati.

Specifica dei parametri di navigazione

La versione attuale di Protocode si basa su un modello piuttosto elementare di navigazione. Tale

modello consente esclusivamente la specifica della destinazione di un’azione di navigazione dato il

contesto di partenza. Un modello migliore permetterebbe la specifica di parametri da passare al

destinatario dell’azione di navigazione. Tale modello permetterebbe ad esempio di passare alla

schermata di destinazione di un’azione di navigazione avente origine in un elemento di una lista

l’elemento del modello utilizzato per generare l’elemento della lista stessa. E’ possibile estendere

MobileCodeGenerator e Protocode in modo da permettere il passaggio di elementi del scene model

durante le azioni di navigazione. Tale estensione tuttavia esula dagli scopi di questo lavoro e viene

lasciata per estensione futura.

Interazione con API

Eccetto per l’interazione con le API di Firebase Realtime Database, la versione attuale di Protocode

e MobileCodeGenerator non permette la specifica dell’interazione con API esterne. L’estensione di

tali strumenti è possibile per quanto riguarda l’interazione con API di tipo REST: esattamente come

viene prototipizzata la forma del modello, è possibile introdurre una prototipizzazione delle risorse

gestite tramite REST API. Tale prototipizzazione, se include anche la definizione dell’URL di ciascuna

risorsa, consentirebbe la generazione automatica del codice di interfaccia con l’API REST grazie alla

struttura ben nota di tale schema. In generale tuttavia la generazione di codice di interazione con

un API di struttura non standard richiede uno sforzo di prototipizzazione complesso, che

renderebbe l’utilizzo di Protocode poco pratico ed efficiente.

131

La struttura a layer dell’applicazione e la modalità di definizione delle interfacce tra i vari layer sono state

progettate per essere facilmente adattabili alle esigenze delle diverse applicazioni, in particolare per

quanto riguarda il superamento di tali limitazioni.

132

7 Conclusioni Lo sviluppo degli strumenti presentati in questo lavoro di tesi e l’attività di valutazione svolta mostrano

pregi e limitazioni di uno sviluppo model - driven di applicazioni mobili cross - platform. Sicuramente, gli

strumenti precedentemente implementati costituivano un ottimo punto di partenza la cui efficacia nella

generazione delle componenti Model e View era già stata dimostrata dai lavori di tesi dei precedenti

sviluppatori. Questo progetto, oltre ad aggiornare ed attualizzare i due strumenti, ha aggiunto la possibilità

di modellare parte della componente Controller, ovvero parte della logica applicativa, ed introdotto la

generazione di codice cross-platform per ReactNative. Per quanto riguarda le estensioni al modello, esse

permettono allo sviluppatore di dedicare il proprio tempo a quegli aspetti della logica applicativa che

richiedono maggiori competenze grazie all’automazione della generazione delle funzionalità più semplici e

più standard da un punto di vista degli approcci adottati. Esempio principe di funzionalità di questa famiglia

è la generazione delle connessioni da View a Model, mediate dal Controller, per l’aggiornamento del model

stesso in base a informazioni introdotte dall’utente attraverso la View stessa. Per quanto riguarda la

generazione di codice cross-platform invece, l’adozione di un approccio non nativo consente di unificare lo

sviluppo per piattaforme diverse in un unico progetto, con conseguente miglioramento nell’utilizzo del

tempo e delle risorse.

L’approccio model driven alla base di entrambi gli strumenti è evidentemente vantaggioso, in quanto

consente una buona compressione dei tempi di sviluppo grazie all’efficace riuso dell’attività di design

dell’applicazione stessa. Rispetto ad uno strumento di prototipizzazione tradizionale, Protocode è

difficilmente utilizzabile al massimo delle sue potenzialità da un team puramente dedicato alla

progettazione di interfacce e interazioni con l’utente (e quindi dotato di competenze attinenti all’area del

design più che all’area dell’implementazione), ma in caso di sinergie tra team di design e team di sviluppo è

possibile utilizzare anche gli strumenti dedicati alla prototipizzazione di Model e Controller, con il fine di

produrre un modello il più ricco e completo possibile. La completezza del modello rispetto ai requisiti

comporta una maggior efficacia delle generazione del codice (efficacia misurabile come rapporto tra la

quantità di codice generato automaticamente e la quantità di codice totale dell’applicazione completa).

Certo è che l’efficacia dell’approccio basato su modelli non può raggiungere il 100% se non in casi

intenzionalmente semplici. Infatti non è possibile includere nella modellazione la logica applicativa, che

costituisce il cuore stesso dell’applicazione. L’inclusione della possibilità di modellare dettagliatamente la

logica applicativa, oltre a portare un significativo aumento della complessità d’uso degli strumenti

presentati, è un’estensione non migliorativa dal punto di vista dell’efficacia temporale: la definizione grafica

di un qualsiasi algoritmo della logica applicativa richiede infatti un tempo maggiore rispetto

all’implementazione da parte di un programmatore. Ciò non toglie tuttavia la possibilità di automatizzare in

parte la generazione della logica applicativa, almeno per quelle componenti che presentano una struttura

fissa e ripetitiva: le connessioni tra eventi di modifica di dati di tipi elementari nella vista e il

corrispondente aggiornamento del modello. Questo lavoro di tesi dimostra infatti la possibilità di generare

automaticamente il codice che implementa tali operazioni, utilizzando pattern largamente supportati dalla

community ed accuratamente analizzati. Da un punto di vista dell’efficacia dell’approccio model driven è

quindi possibile affermare che esso costituisce un’ottima soluzione per comprimere i tempi di sviluppo, ma

che non è possibile generare automaticamente un’applicazione completa, se non in casi molto semplici.

L’intervento dello sviluppatore è comunque richiesto solo per aspetti centrali della logica applicativa, e

questo consente un focus ottimale dello sviluppatore e un utilizzo ottimale del tempo.

La generazione di codice cross-platform rispetto alla generazione di codice nativo presenta aspetti positivi e

negativi. È noto e intuitivo che lo sviluppo di un unico progetto cross-platform, rispetto allo sviluppo

133

autonomo di due progetti nativi, sia vantaggioso sia da un punto di vista dei tempi e delle risorse

(soprattutto umane) richiesti, sia da un punto di vista delle skill che il team di sviluppo deve possedere: la

conoscenza del linguaggio JavaScript, strumento principe degli approcci cross-platform, è sicuramente più

diffusa rispetto alla conoscenza del linguaggio Swift, ad esempio. In questo lavoro di tesi è stato anche

dimostrato come la piattaforma utilizzata (React Native) non sia, attualmente, confrontabile con un

approccio nativo in termini di capacità espressiva: l’eccezione più evidente alla loro equivalenza è

rappresentata dall’impossibilità di utilizzare efficacemente il multithreading, fondamentale in molti contesti

per ragioni di performance e responsività dell’interfaccia grafica. In conclusione, un approccio cross-

platform è indicato, allo stato attuale di sviluppo degli strumenti, per applicazioni che non svolgono

operazioni costose da un punto di vista computazionale o di utilizzo della rete, quali, ad esempio, algoritmi

complessi (dal punto di vista della complessità astratta di un algoritmo, non della sua implementazione),

upload e download di file di dimensione considerevole. È possibile che in futuro una maturazione del

framework risolva questa disparità espressiva e consenta uno sviluppo più efficace.

La validità degli strumenti presentati è stata verificata con un singolo caso di test studiato per esercitare le

diverse funzionalità, ma comunque piuttosto semplice per avere un buon focus sulle feature introdotte da

questo lavoro di tesi. Questa attività di verifica andrebbe ampliata tramite l’utilizzo intensivo degli

strumenti presentati nello sviluppo di applicazioni diverse. Questo consentirebbe di evidenziare al meglio i

punti sia di forza che di debolezza dell’approccio presentato. È comunque evidente l’unicità dell’approccio

presentato da questo progetto, così come la sua utilità nella generazione di codice ripetitivo ed incline a

presentare difetti di implementazione (difetti che spesso nascono dalla superficialità di

un’implementazione ripetitiva). In un mercato in cui la competitività è molto alta, la rapidità e la precisione

garantite da questi strumenti possono rappresentare un punto di forza per un’azienda. Inoltre, tali

strumenti favoriscono l’avvicinamento al mondo delle mobile apps anche da parte di chi non ha una

preparazione completa nel settore. Una conoscenza di base, anche acquisita attraverso lo sviluppo web, è

sufficiente a poter utilizzare Protocode e MobileCodeGenerator, soprattutto utilizzando ReactNative come

framework di destinazione. Dal punto di vista invece delle performance, sebbene l’applicazione sia alla fine

prodotta utilizzando il linguaggio JavaScript, si può affermare che esse siano comparabili con le prestazioni

native grazie al motore di rendering fornito da ReactNative: la View, sebbene descritta utilizzando il

linguaggio JavaScript utilizza controlli nativi a tutti gli effetti, semplicemente composti da un orchestatore

non nativo. Questo consente di ottenere una maggiore fluidità nell’interazione con l’utente grazie alle

performance garantite dalle implementazioni native stesse.

Il bacino di utenza degli strumenti presentati contiene sia sviluppatori alle prime armi, che possono trovare

in Protocode e MobileCodeGenerator un valido aiuto nella realizzazione di pattern complessi o comunque

delicati, sia sviluppatori esperti, che possono trarne vantaggio generando uno scheletro dell’applicazione

completo di tutte le parti più ripetitive e concentrarsi così solamente sugli aspetti più critici della logica

applicativa. Per un’azienda, questo si traduce nel poter destinare personale con competenze critiche a

compiti per cui tali competenze siano effettivamente richieste, e a demandare la produzione del codice di

base ad uno strumento automatico utilizzabile anche dal team che si occupa di design, prototipizzazione e

studio della user experience, eventualmente coadiuvato da sviluppatori con meno esperienza.

7.1.1 Sviluppi futuri

In un settore come quello informatico, e ancor più nel campo delle applicazioni mobili, l’evoluzione degli

strumenti, degli standard e dei framework è estremamente rapida. Questo si traduce nella necessità di

produrre continui aggiornamenti sia per offrire nuove features che per adottare tecnologie nuove, più

efficaci e più performanti. Un fronte su cui certamente MobileCodeGenerator è chiamato ad evolvere è

134

quello dei linguaggi e dei framework di destinazione. Google ha già rilasciato il linguaggio Kotlin per

Android, e potrebbe essere sicuramente molto interessante valutare come l’adozione di questo linguaggio

possa modificare la performance della generazione del codice e le funzionalità generabili automaticamente.

Dal punto di vista delle funzionalità offerte, entrambi i tools potrebbero essere migliorati aggiungendo il

supporto a parti del controller che nella versione attuale sono solamente inserite in bozza, come ad

esempio il passaggio di parametri nella navigazione o la persistenza dei dati utilizzando API REST. Un

ulteriore step potrebbe essere l’introduzione della modellazione esplicita della navigazione, attraverso

l’aggiunta di un editor dedicato alla struttura della navigazione stessa.

Infine, per quanto riguarda l’architettura software, potrebbe essere interessante ridurre le dipendenze del

progetto integrando il generatore di codice nell’app Protocode. La tecnologia su cui si basa

MobileCodeGenerator è infatti datata e poco documentata nelle ultime revisioni di Eclipse, e lo stesso

Eclipse è una dipendenza piuttosto scomoda per il progetto. Esistono ad oggi diversi strumenti che

consentono l’espansione di templates direttamente in Javascript. Questo eliminerebbe la necessità di

esportare il modello in formato XML, in quanto il generatore potrebbe appoggiarsi direttamente allo stato

dell’applicazione Protocode, e sposterebbe l’esecuzione del workflow da Java al browser, meno efficiente

ma certamente meno pesante dal punto di vista delle risorse richieste, data la semplicità concettuale del

processo di espansione di un template.

135

8 Bibliografia

[1] We Are Social, «Global Digital Report 2019,» [Online]. Available: https://wearesocial.com/global-

digital-report-2019. [Consultato il giorno 21 03 2019].

[2] Gartner, «Newsroom,» 08 2018. [Online]. Available: https://www.gartner.com/en/newsroom/press-

releases/2018-08-28-gartner-says-huawei-secured-no-2-worldwide-smartphone-vendor-spot-

surpassing-apple-in-second-quarter. [Consultato il giorno 21 03 2019].

[3] G. Perego e S. Pezzetti, Un approccio model-driven per lo sviluppo di applicazioni mobili native, Master

thesis, Politecnico di Milano, 2013.

[4] M. Natali, Prototipizzazione rapida per applicazioni mobili multipiattaforma, Master thesis, Politecnico

di Milano, 2014.

[5] A. Pintus, Generazione automatica di applicazioni mobili native secondo un approccio model-driven,

Master thesis, Politecnico di Milano, 2016.

[6] A. Rossotti, Prototipizzazione e generazione automatica della componente Model per applicazioni

mobili multipiattaforma, Master thesis, Politecnico di Milano, 2017.

[7] «Proto.io,» [Online]. Available: https://proto.io/.

[8] «Sketch,» [Online]. Available: https://www.sketch.com/.

[9] «PhotoShop,» [Online]. Available: https://www.adobe.com/Photoshop.

[10] «MarvelApp,» [Online]. Available: https://marvelapp.com/.

[11] «Adobe Experience Design CC,» [Online]. Available: https://www.adobe.com/it/products/xd.html.

[12] «Xamarin,» [Online]. Available: https://visualstudio.microsoft.com/it/xamarin/.

[13] «Microsoft,» [Online]. Available: https://www.microsoft.com/it-it.

[14] «Visual Studio,» [Online]. Available: https://visualstudio.microsoft.com/it/.

[15] «PhoneGap,» [Online]. Available: https://phonegap.com/.

[16] «Adobe,» [Online]. Available: https://www.adobe.com/.

[17] «Apache Cordova,» [Online]. Available: https://cordova.apache.org/.

[18] «Appcelerator Titanium,» [Online]. Available: https://www.appcelerator.com/.

[19] «React Native,» [Online]. Available: https://facebook.github.io/react-native/.

136

[20] «DecoIde,» [Online]. Available: https://www.decoide.org/.

[21] «Android Studio,» [Online]. Available: https://developer.android.com/studio.

[22] «Apple XCode,» [Online]. Available: https://developer.apple.com/xcode/.

[23] E. Umuhoza, «Domain-specific modeling and code generation for cross-platform multi-device mobile

apps,» CoRR, 2015.

[24] E. Umuhoza e M. Brambilla, «Model driven development approaches for mobile applications: A

survey,» in Mobile Web and Intelligent Information Systems - 13th International Conference, MobiWIS

2016, Vienna, Austria, 2016.

[25] E. Umuhoza, M. Brambilla, H. Ed-douibi, J. Cabot e A. Bongio, «Automatic code generation for cross-

platform, multi-device mobile apps: Some reflections,» in Proceedings of the 3rd International

Workshop on Mobile Development Lifecycle, New York, NY, USA, 2015.

[26] «ReactJS,» [Online]. Available: https://reactjs.org/.

[27] «Yarn,» [Online]. Available: https://yarnpkg.com/lang/en/.

[28] Redux. [Online]. Available: https://redux.js.org/.

[29] «Bootstrap,» [Online]. Available: https://getbootstrap.com/.

[30] «React Router,» [Online]. Available: https://reacttraining.com/react-router.

[31] «FontAwesome,» [Online]. Available: https://fontawesome.com/.

[32] «FileSaver,» [Online]. Available: https://github.com/eligrey/FileSaver.js/.

[33] «XML Beautifier,» [Online]. Available: https://www.npmjs.com/package/xml-beautifier.

[34] «UniqId,» [Online]. Available: https://www.npmjs.com/package/uniqid.

[35] «Babel JS,» [Online]. Available: https://babeljs.io/.

[36] «Eclipse Modeling Framework,» [Online]. Available: https://www.eclipse.org/modeling/emf/.