-
Notifications
You must be signed in to change notification settings - Fork 0
/
PhoneGapManuale.tex
1795 lines (1360 loc) · 125 KB
/
PhoneGapManuale.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\documentclass[10pt,a4paper,onecolumn]{article}
\usepackage[utf8]{inputenx}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{listings}
\usepackage{textcomp}
\usepackage[italian]{babel}
\usepackage{amsmath}
\usepackage{booktabs}
\usepackage{graphicx}
\usepackage[font=small,labelfont=bf,labelsep=period,tableposition=top]{caption}
\usepackage{tabularx}
\usepackage{multirow}
\usepackage{booktabs}
\usepackage{longtable}
\usepackage{fancyhdr}
\usepackage{lastpage}
\usepackage{color}
\usepackage{amsfonts}
% **************************************************
% Tabelle
% **************************************************
\usepackage{tabularx} % tabelle di larghezza fissa con una o più colonne variabili
\usepackage{multirow} % colonne con colonne che si estendono per più righe
\usepackage{booktabs} % per inserire l'ambiente table e le righe orizz. nelle tabelle
\usepackage{longtable} % tabelle oltre i limiti di pagina
\usepackage[table,usenames,dvipsnames]{xcolor} % tabelle con righe colorate e alternate
% ***************************************
% Parametri di formattazione della pagina
% ***************************************
\usepackage[a4paper,head=4cm,top=4.5cm,bottom=3cm,left=3.5cm,right=3.5cm,bindingoffset=5mm]{geometry}
% **************************************************
% Definizioni di colori
% **************************************************
\definecolor{myBlue}{RGB}{1,167,236}
\definecolor{lightblue}{RGB}{213,243,253}%{119,218,247}
\definecolor{llightblue}{RGB}{229,255,255}
\fancyhead{}
\renewcommand{\headrulewidth}{1pt}
\fancyhead[RE,RO]{
\begin{picture}(-135,0)
\put(-517,0){\includegraphics[width=0.1\textwidth]{img/cordova}}
\put(-470,12){\sffamily\large\leftmark}
\end{picture}
}
\cfoot{}
\fancyfoot[RO,LE]{\sffamily Pag.~\thepage{} di \pageref{LastPage}}
\fancyfoot[RE,LO]{\emph{Sencha \& PhoneGap }}
\renewcommand{\footrulewidth}{.2pt}
\pagestyle{fancy}
\renewcommand{\sectionmark}[1]{\markboth{#1}{#1}}
% **************************************************
% Cross-references e collegamenti ipertestuali
% **************************************************
\usepackage[hidelinks]{hyperref}
\hypersetup{%
colorlinks=false, linktocpage=false, pdfborder={0,0,0}, pdfstartpage=1, pdfstartview=FitV,%
urlcolor=Cyan, linkcolor=Cyan, citecolor=Black, %pagecolor=Black,
pdfcreator={pdflatex}, pdfproducer={pdflatex with hyperref package}%
}
\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
\lstset{ %
language=java, % the language of the code
basicstyle=\footnotesize, % the size of the fonts that are used for the code
numbers=left, % where to put the line-numbers
numberstyle=\tiny\color{gray}, % the style that is used for the line-numbers
stepnumber=2, % the step between two line-numbers. If it's 1, each line
% will be numbered
numbersep=5pt, % how far the line-numbers are from the code
backgroundcolor=\color{white}, % choose the background color. You must add \usepackage{color}
showspaces=false, % show spaces adding particular underscores
showstringspaces=false, % underline spaces within strings
showtabs=false, % show tabs within strings adding particular underscores
frame=single, % adds a frame around the code
rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here))
tabsize=2, % sets default tabsize to 2 spaces
captionpos=b, % sets the caption-position to bottom
breaklines=true, % sets automatic line breaking
breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace
title=\lstname, % show the filename of files included with \lstinputlisting;
% also try caption instead of title
keywordstyle=\color{blue}, % keyword style
commentstyle=\color{dkgreen}, % comment style
stringstyle=\color{mauve}, % string literal style
escapeinside={\%*}{*)}, % if you want to add LaTeX within your code
morekeywords={*,...}, % if you want to add more keywords to the set
deletekeywords={...} % if you want to delete keywords from the given language
}
\begin{document}
%----------------------------------------------------------
\begin{titlepage}
\begin{center}
% Upper part of the page
\textsc{\Large}\\[6cm]
\includegraphics[width=0.9\textwidth]{img/fronte.png}\\[0.3cm]
\noindent\rule{\textwidth}{0.4pt} \\[0.3cm]
\textsc{\Large Sencha Touch \& PhoneGap}\\[0.1cm]
\noindent\rule{\textwidth}{0.4pt}\\[0.5cm]
\textit{``Sviluppo di applicazioni mobile ibride''} \\[0.5cm]
\textsc{25 luglio 2013}\\[0.5cm]
%\cite{}
% Author and supervisor
\begin{minipage}{0.4\textwidth}
\begin{flushleft} \large
\emph{Autori:}\\
Andrea Rizzi\\
Marco Pezzutti
\end{flushleft}
\end{minipage}
\begin{minipage}{0.4\textwidth}
\begin{flushright} \large
\emph{Mail:} \\
\end{flushright}
\end{minipage}
\end{center}
\end{titlepage}
%-----------------------------------------------------------------------
%----------------------------------------------------------------------------------------
% Indice
%----------------------------------------------------------------------------------------
\clearpage
\pdfbookmark[1]{Indice}{Indice}
\tableofcontents
%----------------------------------------------------------------------------------------
% Indice delle figure
%----------------------------------------------------------------------------------------
\clearpage
\pdfbookmark[1]{Indice delle figure}{Indice delle figure}
\listoffigures
%----------------------------------------------------------------------------------------
% List of Listings
%----------------------------------------------------------------------------------------
\clearpage
\pdfbookmark[1]{Elenco dei listati}{Elenco dei listati}
\lstlistoflistings
\clearpage
\abstract{Il presente documento si prefigge lo scopo di introdurre allo sviluppo di applicazioni ibride, mediante l'uso dei framework Sencha Touch e PhoneGap. Rimandando più volte ad approfondimenti esterni, il documento tende a privilegiare le best practice da tenere in considerazione, fornendo comunque più alternative a seconda dei casi d'implementazione. \\Particolare attenzione sarà data alle tecniche di archiviazione dei dati sul device e a come eseguire una sincronizzazione con un database remoto.}
\begin{flushright}
\emph{l'autore, Andrea Rizzi}
\end{flushright}
\clearpage
\section{PhoneGap}
\subsection{Cos'è PhoneGap}
\begin{figure}[h]
\centering
\includegraphics[width=0.4\textwidth]{img/logo}
\caption{PhoneGap - logo}
\label{fig:phonegap logo}
\end{figure}
PhoneGap è un framework \textit{cross-platform} mobile che consente di sviluppare delle applicazioni native attraverso l'utilizzo di tecnologie web quali HTML, CSS e JavaScript. In altre parole, PhoneGap consente di ''tradurre'' le web-application in mobile application.
L'architettura di Phonegap prevede uno strato di astrazione del device, che espone le funzionalità mediante un API Javascript utilizzabile dall'applicazione senza conoscere i dettagli del device sottostante. Per ottenere questo risultato esiste una controparte nativa, specifica per piattaforma/sistema operativo, che mappa le funzionalità sulle API specifiche del device.
Il codice dell'applicazione è basato su HTML, CSS e Javascript, esattamente come una mobile web app servita da un web server. In questo caso le risorse fanno parte degli asset locali distribuiti insieme all' applicazione nativa, che (semplificando) utilizza un istanza dell'oggetto UIWebView come "container" per eseguire l'applicazione e comunicare con il layer nativo. Questo tipo di approccio è noto come hybrid mobile app.
Qualora le API disponibili con Phonegap non fossero sufficienti, il framework è estendibile grazie ad un'architettura a plug-in, che consente di sviluppare nuove funzionalità, sviluppandone la controparte nativa ed esponendole via Javascript. Questo oltre a rappresentare una possibilità, in un contesto cross-platform potrebbe rivelarsi anche una limitazione, dal momento che occorre sviluppare una controparte nativa del Plug-in per ogni piattaforma/sistema operativo mobile che si desidera supportare.
Di seguito i plug-in già disponibili per i diversi Apple iOS:
\begin{itemize}
\item Facebook Connect;
\item Barcode scanner;
\item Push notification;
\item PDF viewer;
\end{itemize}
Il repository ufficiale, basato su GitHub, contenente i Plug-in di Phonegap è disponibile all'indirizzo: \url{https://github.com/phonegap/phonegap-plugins}.
La guida completa per lo sviluppo di plug-in nativi (l'argomento non è trattato in questo articolo), è disponibile su \url{http://docs.phonegap.com/en/2.2.0/guide_plugin-development_index.md.html}.
\subsection{Funzionalità e dispositivi supportati}
Alla data in cui viene scritto questo manuale, PhoneGap è alla versione 2.7.0, e può essere utilizzato sulle piattaforme:
\begin{itemize}
\item Android;
\item iPhone;
\item Blackberry;
\item WebOS;
\item Windows Phone 7 ed 8;
\item Symbian;
\item Bada;
\end{itemize}
Nello specifico sono supportate le \textit{feature}:
\begin{figure}[h]
\centering
\includegraphics[width=1\textwidth]{img/Funzionalita}
\caption{PhoneGap v2.7.0 - funzionalità supportate}
\label{fig:funzionalita}
\end{figure}
\subsection{Sistema di build delle applicazioni}
Come già detto PhoneGap può essere visto come un framework atto a trasformare delle web application in applicazioni mobile fruibili su più piattaforme. Il build dell'app può avvenire in due modi:
\begin{itemize}
\item \textbf{utilizzo di piattaforme specifiche}: la creazione di un applicazione con PhoneGap richiederà strumenti di sviluppo particolari a seconda della piattaforma su cui vogliamo rendere disponibile l'app. Per esempio se si volesse distribuire l'app su piattaforme iPhone, sarebbe necessario disporre di un computer apple con installato Xcode. Il sistema che andremmo a descrivere, si basa sull'uso di una piattaforma windows con lo scopo di distribuire l'app su sistemi android;
\item \textbf{utilizzo di un build online}: Adobe ha creato un sistema di build online che permette di caricare il proprio codice sorgente o mediante un upload diretto, oppure specificando l'indirizzo di un repository Git contenente l'applicazione mobile. Questa scelta sicuramente comoda e da preferirsi alla prima, presenta però lo svantaggio di essere a pagamento, e di fornire gratuitamente il build di un unica applicazione. Per ulteriori informazioni rimandiamo al sito online: \url{https://build.phonegap.com/}.
\end{itemize}
\clearpage
\section{Configurare l'ambiente di lavoro}
I seguenti step per la configurazione dell'ambiente di lavoro prevedono l'utilizzo di un computer con sistema operativo Windows 7.
Lo sviluppo delle applicazioni si baserà sull'uso del framework JavaScript Sencha Touch 2, idealmente pensato per lo sviluppo mobile.
La soluzione proposta è ottenuta con strumenti gratuiti. Solo al termine di tale sezione saranno proposte alternative a pagamento.
\subsection{Strumenti necessari}
Prima di procedere con l'installazione e la configurazione dell'ambiente di lavoro, si raccomanda di effettuare il download del seguente materiale:
\begin{itemize}
\item \textbf{JDK}: per sviluppare le nostre mobile application necessitiamo del Java Delelopment Kit, scaricabile all'indirizzo \url{http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html}. Si osservi che la JDK contiene già la JRE, che di conseguenza non dovrà essere scaricata. Alla data corrente, l'ultima versione disponibile (che corrisponde anche a quella usata) è la 7. Nel sito troverete il link al download nella lista \textbf{Java SE Development Kit 7u21}. Nel nostro caso è stata usata quella per Windows x64;
\item \textbf{IDE Eclipse}: il software è reperibile al sito \url{http://www.eclipse.org/downloads/}. Per quanto la scelta tra le diverse versioni non dovrebbe comportare alcun problema, si raccomanda il download di \textbf{Eclipse IDE for Java EE Developers}. Alla data corrente, l'ultima versione (che corrisponde anche a quella utilizzata in questa guida) è Juno (4.2);
\item \textbf{PhoneGap SDK}: potrete scaricare l'ultima versione direttamente dal sito ufficiale di PhoneGap: \url{http://phonegap.com/download/};
\item \textbf{ANT}: potrete scaricare il software direttamente dall'indirizzo \url{http://ant.apache.org/bindownload.cgi};
\item \textbf{Android SDK}: potrete scaricare il software direttamente dal sito \url{http://developer.android.com/sdk/index.html};
\end{itemize}
Ora se siete intenzionati a sviluppare le vostre applicazioni con Sencha Touch 2, come proposto in questo manuale, necessitate di scaricare anche la sua SDK. Potrete reperirla direttamente dal sito ufficiale: \url{http://www.sencha.com/products/touch/download/}. Qui, fornendo il vostro indirizzo e-mail, potrete scaricare la versione gratuita. Se si desidera un ambiente di sviluppo completo, si può scaricare la versione a pagamento che fornisce:
\begin{itemize}
\item Sencha Architect;
\item Sencha Ext JS;
\item Sencha Touch (si tratta del SDK che potete scaricare gratuitamente al link citato in precedenza);
\item Sencha Eclipse Plugin;
\item Sencha Touch Charts;
\item Sencha Mobile Packaging;
\item Enterprise Data Connectors;
\item Sencha Support Package;
\end{itemize}
Bene, ora siamo pronti per iniziare!
\subsection{Installazione step by step}
\textbf{ATTENZIONE}: come vedrete in seguito, sarà necessario creare delle variabili di sistema. Ciò richiederà da parte vostra di inserire il percorso a determinati file. Dunque è importante tenere traccia dei percorsi d'installazione dei vari componenti.
\begin{description}
\item[Step 1 - installare la JDK]: portiamoci nella cartella in cui abbiamo scaricato la JDK e avviamo l'installazione. Come già detto premuriamoci di prendere nota del path di installazione.
\item[Step 2 - installare IIS]: andate dal menù Start e aprite il pannello di controllo. Quindi selezionate la voce programmi e dal menù che comparirà selezionate la voce Funzionalità di windows. Dopo un breve caricamento comparirà un elenco di funzionalità aggiuntive del sistema. Cercare la voce Internet Informarion Services, espandete l'elenco e selezionate Servizi Web e Strumenti di gestione Web. Terminate quindi l'installazione premendo su OK. Poi procedete aprendo ISS manager (ricercabile dalla strumento di ricerca presente nello start). Qui dovrete aggiornare il catalogo MIME aggiungendo l'estensione 'JSON' per le applicazioni 'application/json'. Se avete dubbi su tale procedura, potete consultare la discussione \url{http://stackoverflow.com/questions/332988/get-iis6-to-serve-json-files-inc-post-get/1121114#1121114 }.
\item[Step 3 - installare Eclipse]: come per la JDK, portiamoci nella cartella in cui abbiamo scaricato Eclipse e scompattiamo l'applicativo nella cartella Programmi di windows. L'applicativo non richiede installazioni ed è già pronto per l'esecuzione.
\item[Step 4 - installare Android-SDK]: avviate l'installazione della SDK scaricata in precedenza. Quindi aprite la SDK manager e cercate nell'elenco l'ultima versione di Android. Questa dovrebbe risultare già installata. Se cosi non fosse, spuntate la check box adiacente a tale voce e procedete con l'installazione. Quindi, sempre da SDK manager, spuntate la voce Tools e avviate l'installazione.
\item[Step 5 - installare il plugin Android ADT]: aprite Eclipse e dal menù a tendina seguite il percorso Help > Install New Software... quindi nella text box nominate 'Work With' scrivete https://dl-ssl.google.com/android/eclipse. Quando vedrete comparire l'opzione install Developer Tools, cliccate su di essa e avviate l'installazione di tutti i componenti correlati a tale voce.
\item[Step 6 - configurare le variabili d'ambiente] : come accennato all'inizio di questa sezione, è venuto il momento di impostare le variabili di sistema necessarie. Quindi facciamo click destro sull'icona 'Computer' (se non è presente un collegamento sul vostro Desktop, potrete trovarla nell'esplora risorse sulla colonna di sinistra). Procediamo selezionando la voce 'Proprietà'. Quindi andiamo su 'Impostazioni di sistema avanzate'. Si aprirà una finestra contenente un group panel. Andate alla voce avanzate e cercate il bottone nominato 'Variabili d'ambiente'. Cliccatelo e aggiungete nelle variabili di sistema la variabile JAVA\_HOME. Se per un qualche motivo non avete preso nota della cartella d'installazione di java, potete provare a cercare in \verb|C:\Program Files\Java\jdk1.7.0_21|.
Ora cercate la variabile di sistema denominata path e aggiungetevi i seguenti percorsi (ogni percorso va separato da un ; ) relativi ai file:
\begin{itemize}
\item javac;
\item ant;
\item adb (NOTA: il percorso è relativo ad adb.exe, e lo troverete nella cartella platform-tools presente nella directory d'installazione della andrid sdk);
\item android (NOTA: troverete il file nella cartella d'installazione della SDK di Andorid).
\end{itemize}
Se avete trovato difficoltà in tale sezione potete leggere il toutorial \url{http://simonmacdonald.blogspot.ca/2012/11/getting-create-command-to-work-on.html} che riporta delle soluzioni a tali problematiche.
\end{description}
Congratulazioni, a questo punto l'ambiente di lavoro è correttamente configurato!
\clearpage
\section{Sencha Touch 2}
\subsection{Introduzione al framework}
\begin{figure}[h]
\centering
\includegraphics[width=0.2\textwidth]{img/sencha}
\caption{Sencha Touch - logo}
\label{fig:sencha logo}
\end{figure}
Sencha Touch è un framework JavaScript avente architettura MVC, e il suo utilizzo è particolarmente adatto alla realizzazione di mobile application ibride. Tra le sue caratteristiche c'è la capacità di sviluppare applicazioni con il look and fell nativo delle principali piattaforme mobile. Tutto ciò avviene semplicemente caricando un apposito css nella pagina index.html dell'applicativo.
Sencha basa il proprio funzionamento sull'Ext.JS e dispone di alcune componenti grafiche appositamente ottimizzate per un input di tipo touch.
Volendo fornire una visione generale dell'architettura di Sencha, evidenziamo le seguenti componenti:
\begin{itemize}
\item \textbf{Ext.Container - [View]}: sono le viste della nostra applicazione e sono definibili come dei contenitori di componenti grafici;
\item \textbf{Ext.data.Model - [Model]}: rappresentano il modello dei dati. Quando definiamo un model definiamo i campi che lo caratterizzano e la loro natura (tipologia, parametri di default, opzionalià di inserimento, ecc...). Idealmente possiamo pensare ad un model come allo schema di una tabella di un database. Quando definiamo un Model dobbiamo definire anche la \textbf{tipologia della sorgente} fisica da cui estraiamo e su cui memorizziamo i dati.
\item \textbf{Ext.data.proxy.Proxy - [Proxy]}: rappresenta la tipologia di sorgente fisica espressa al punto precedente. Un proxy in associazione ad un Model, definisce come i dati vengono gestiti fisicamente dal browser e (nel caso siano usati su una applicazione ibrida) dal dispositivo portatile. Per esempio un proxy può essere di tipo JsonP, stabilendo quindi che la sorgente dei dati è fisicamente presente in un server esterno al dominio in cui risiede l'applicativo.
\item \textbf{Ext.data.Srore - [Store]}: rappresenta un "contenitore" per la memorizzazione di dati. i dati memorizzati devono essere conformi allo schema espresso da un model, dichiarato in fase di definizione dello Store. Uno Store è un oggetto che può essere facilmente associato ad un componente della vista (come per esempio una lista). Sencha predispone una relazione Store-Vista simile a quella definita dal pattern observer, e garantisce quindi una sincronizzazione automatica della vista al variare dei dati presenti nello store.
\item \textbf{Ext.app.Controller - [Controller]}: è l'oggetto contenente la logica di gestione dei dati. In una buona programmazione, il controller dovrebbe essere colui che fa interagire la vista con i dati sottostanti (pensando ad un'architettura MVC) e che sa far corrsispondere ad ogni evento il giusto gestore.
\end{itemize}
\subsection{L'architettura delle classi}
Prima di iniziare con la trattazione del argomento, informo il lettore che quanto mi accingo a trattare costituisce un breve riassunto del elaborato ``The Sencha Class System'', aggiornato con quanto da me sperimentato durante lo stage.
Detto ciò voglio ricordare che Sencha Touch non è un linguaggio di programmazione object oriented. Pertanto egli non ha un concetto di programmazione a classi ben definito come in altri linguaggi (vedi per esempio Java). Sencha cerca tuttavia di predisporre degli oggetti il cui funzionamento e scopo emulano quasi alla perfezione quello delle classi, dando un esperienza d'uso che facilita lo sviluppatore nel compito di imporre una buona organizzazione strutturale al codice dell'applicativo. Nello specifico Sencha predispone un oggetto base denominato Ext.Class a cui possiamo riferirci quando, l'oggetto che stiamo definendo non deve ereditare nessun oggetto particolare già esistente. Per creare tali entità è possibile usare una sintassi simile alla seguente:
\lstset{language=java,caption={Esempio creazione di una Ext.Class in Sencha},label=DescriptiveLabel}
\begin{lstlisting}
var Person = new Ext.Class({
name: 'Mr. Unknown',
walk: function(steps) {
alert(this.name + ' is walking ' + steps + ' steps');
}
});
\end{lstlisting}
Come si può osservare l'oggetto Ext.Class accetta come unico parametro un oggetto costituito da coppie chiave-valore. In tale contesto le chiavi devono essere univoche e i valori associati possono essere sia oggetti, tipi primitivi o funzioni. In un certo senso cosi facendo è possibile definire ``inline'' la struttura di un oggetto specificandone le variabili e i metodi pubblici. Mediante la \emph{keyword}.
Come ogni classe degna di questo nome, anche in Sencha è possibile definire un costruttore per l'inizializzazione delle istanze della classe. Per farlo si usa la \emph{keyword} come nel seguente modo:
\lstset{language=java,caption={Definire un costruttore per le classi Sencha},label=DescriptiveLabel}
\begin{lstlisting}
var Person = new Ext.Class({
name: 'Mr. Unknown',
//costruttore pubblico della classe
constructor: function(name) {
this.name = name;
return this;
},
walk: function(steps) {
alert(this.name + ' is walking ' + steps + ' steps');
}
});
//creo un'istanza di persona usando il costruttore
var jacky = new Person('Jacky');
jacky.walk(10); // alerts "Jacky is walking 10 steps"
\end{lstlisting}
\subsubsection{Ext.define()}
Ovviamente un sistema di definizione delle classi come quello proposto in precedenza è sconsigliabile, e da usare solo quando Sencha non ci permette di usare l'oggetto Ext.define(). Questo infatti ci permette di definire lo schema di un oggetto (ci permette di emulare una classe) in un file separato dal resto del codice in cui l'oggetto viene istanziato. Ext.define() accetta 3 parametri:
\begin{itemize}
\item una stringa rappresentante il nome della classe;
\item un oggetto analogo a quello usato in Ext.Class, contente le coppie chiave-valore usate per definire la struttura dell'oggetto;
\item una funzione di callback da invocare quando tutte le dipendenze della classe sono state risolte;
\end{itemize}
Un esempio di utilizzo di tale sistema è il seguente:
\lstset{language=java,caption={Usare Ext.define()},label=DescriptiveLabel}
\begin{lstlisting}
Ext.define('My.sample.Person', {
name: 'Mr. Unknown',
constructor: function(name) { /* ... */ },
walk: function(steps) { /* ... */ }
});
\end{lstlisting}
Per istanziare un oggetto a partire da unaclasse cosi definita, è sufficiente utilizzare la già citata funzione Ext.Class(), passando:
\begin{itemize}
\item come primo parametro il nome completo della classe o l'alias con cui è stata definita;
\item come secondo parametro (se la classe lo richiede) l'oggetto di configurazione usato dal costruttore ridefinito.
\end{itemize}
\subsubsection{Namespacing}
Come nella progettazione di ogni applicativo, usare un approccio orientato agli oggetti ci permette di definire le classi cui si compone l'applicativo stesso. Mediante il sistema di emulazione delle classi fornito da Ext.define() ciò è possibile anche con Sencha. Di conseguenza possiamo strutturare le componenti dell'applicazione mediante una suddivisione in package e l'ovvio ordinamento dei nomi mediante namespacing. Ciò si ripercuote sulla nomenclatura che usiamo sulla classe. Tale infatti deve rispettare la posizione che essa ha nel package in cui è collocata. Per esempio se abbiamo il package MyCompany/MyTool/yClass, la classe MyClass dovrà essere nominata nel Ext.define() come: MyCompany.MyTool.MyClass.
\subsubsection{Pre \& post-processors}
Prima di creare ed eseguire una classe da noi definita, vi è un'attività della pre-processors in cui il codice della classe viene esaminato alla ricerca di determinate parole chiave. Tra queste \emph{keyword} cito:
\begin{itemize}
\item \textbf{extend}: property usata per emulare il meccanismo di ereditarietà tra classi;
\item \textbf{mixins}: property usata per far si che la classe definita contenga anche il contenuto delle classi specificate in tale proprietà;
\item \textbf{config}: property in cui vengono definite coppie chiave-valore per le quali il meccanismo di pre-processors definisce metodi di set e di get in automatico;
\item \textbf{requires}: property usata per definire una serie di elementi esterni richiesti dalla classe per i suoi scopi (simile al meccanismo di import delle classi in Java);
\item \textbf{statics}: property che permette di definire delle variabili e delle funzioni statiche;
\end{itemize}
Analogamente ai pre-processors esistono dei post-processors, ossia delle parole chiave (con funzioni ben precise) che vengono processate a run-time solo quando la classe è pronta. Tra queste menziono:
\begin{itemize}
\item \textbf{singleton}: emulazione del pattern singleton, usata quindi per limitare il numero di istanze creabili ad una;
\item \textbf{alternateClassName}: permette di definire un nome alternativo con cui riferirsi alla classe. Generalmente usata per garantire una retro-compatibilità per le classi rinominate;
\item \textbf{alias}: genera un xtypes che viene mappato nel contesto dell'applicazione. Tale alias può essere usato in un qualsiasi punto come \emph{shorthands} (abbreviazione) per riferirsi alla classe stessa.
\end{itemize}
\subsection{Best practice}
\subsubsection{Creare un progetto}
Per creare una buona applicazione e lavorarvi nell'ordine, è importante impostare correttamente l'ambiente di lavoro. Innanzitutto portiamoci nella directory root del web browser su cui lavoriamo. Nel nostro caso la cartella si trova in \verb|C:\intpub\wwwroot|. Qui creiamo una cartella con il nome del nostro progetto (con riferimento all'app che creeremo nella sezione "La mia prima applicazione") NotesApp. Quindi definiamo al suo interno:
\begin{itemize}
\item una cartella app che conterrà la struttura vera e propria dell'applicativo. LA cartella conterrà a sua volta:
\begin{itemize}
\item una cartella view;
\item una cartella controller;
\item una cartella store;
\item una cartella model;
\item (opzionale... diventa obbligatorio se si vuole ridefinire un Ext.data.proxy.*) una cartella proxy.\\
Le cartelle cosi create andranno a contenere i file che definiscono gli omonimi oggetti Sencha precedentemente descritti;
\end{itemize}
\item una cartella lib che conterrà la libreria Sencha Touch 2;
\item una cartella rsc (o resorces se preferite), dove caricheremo i css personali;
\item un file index.html;
\item un file app.js;
\item (per ora facoltativo... diventa obbligatorio se vogliamo trasformare la web app in un app ibrida fruibile su device portatili) copiare il file cordova cordova-2.7.0.js, scaricabile insieme all'ultima versione di PhoneGap.
\end{itemize}
Detto ciò, per dare una trattazione maggiore sull'utilizzo dei vari file creati, rimandiamo alla guida riportata in sezione "La mia prima applicazione".
\subsubsection{In linea generale}
La logica di sviluppo di un applicazione Sencha, non che il sistema con cui si "incastrano" le varie componenti fin qui presentate, dovrebbe essere quella di:
\begin{itemize}
\item progettare e predisporre la grafica dell'applicazione tramite la definizione di un layout e dei componenti costituenti le view.
\item definire i modelli dei dati su cui lavorare, stando attenti a scegliere la tipologia giusta di proxy (lo sviluppatore non si spaventi, passare da un tipo di proxy ad un'altro in corso d'opera, è un azione semplice quanto sostituire una stringa);
\item definire gli store necessari scegliendo con cura se gestirli con un auto load o nel caso assicurandosi di caricarli nel momento giusto;
\item associare gli store alle componenti delle view predisposte per la visualizzazione dei dati;
\item definire degli eventi da associare ai componenti della vista;
\item predisporre un uno o più controller. Idealmente, sia per chiarezza che per manutenibilità, si definisce un controller per ogni view dell'applicazione;
\end{itemize}
\subsubsection{Come definire e sfruttare il meccanismo delle classi}
L'argomento è in se molto corposo, ed una trattazione cosi esaustiva esula dagli scopi di questo documento. Si raccomanda di leggere l'articolo riportato all'indirizzo \url{http://www.sencha.com/learn/sencha-class-system}.
Data l'importanza di alcuni accorgimenti, si riportano di seguito solo alcune delle funzionalità riportate nell'articolo precedentemente citato:
\begin{description}
\item[Config e la definizione automatica di getter e setter]: ogni classe di Sencha definisce al suo interno un oggetto config, strutturato come una raccolta di property. Il framework predispone per ogni property qui definita, un getter ed un setter facente riferimento al nome della property stessa. Quindi se si definisce config{ myName: 'Andrea' }, sarà possibile richiamare pubblicamente getMyName(). Si osservi come la prima lettera della property, nel getter, diventi maiuscola.
\item[Richiamare un metodo di super dentro l'override dello stesso]: se vi trovate ad estendere una classe di Sencha, può essere necessario ridefinire un comportamento specifico per un metodo della superclasse, che al suo interno preveda un caso di default la cui gestione debba essere delegata allo stesso metodo della superclasse (quindi al metodo non sovrascritto). Un esempio potrebbe essere la ridefinizione del metodo load() di uno store. Supponendo di voler gestire in modo diverso il primo load, potremmo sovrascrivere il metodo andando a specificare un comportamento per il nostro caso, richiamando negli altri il load di super. Una procedura del genere si esegue inserendo, all'interno del metodo sovrascritto, l'istruzione \texttt{this.callParent()}. Si osservi infine che se il metodo richiamato necessità di parametri la dicitura diventa \texttt{this.callParent(arguments)}.
\end{description}
\subsubsection{Come usare le associazioni tra modelli}
Sencha permette di definire 3 diverse tipologie di associazione tra modelli:
\begin{itemize}
\item \textbf{hasOne}: serve per indicare che un istanza del modello è associata ad una ed una solo istanza del modello a cui è associata (e.g. una macchina ha uno ed un solo motore -> car hasOne engine);
\item \textbf{hasMany}: serve per indicare un associazione multipla. L'istanza di partenza è associata a zero o più istanze del modello associato (e.g. una macchina ha più ruote -> car HasMany tyre);
\item \textbf{belongsTo}: indica un rapporto di appartenenza unitario, del tipo padre-figlio (e.g una macchina appartiene ad uno ed un solo proprietario -> car belongsOne owner).
\end{itemize}
\begin{figure}[h]
\centering
\includegraphics[width=0.5\textwidth]{img/associazioni}
\caption{Sencha Touch - associazioni tra modelli}
\label{fig:associazioni}
\end{figure}
Quanto detto si traduce in codice nel seguente modo:
\lstset{language=java,caption={Esempio di associazioni tra modelli},label=DescriptiveLabel}
\begin{lstlisting}
Ext.define("MyApp.model.Car", {
extend: 'Ext.data.Model',
config: {
idProperty: 'Id',
fields: [
{ name: 'Id' },
{ name: 'brandName' },
{ name: 'type' },
{ name: 'ownerId' }
],
belongsTo: [{ model: 'MyApp.model.Owner', associationKey: 'ownerId' }],
hasOne: [{ model: 'MyApp.model.Engine', associationKey: 'engineId' }],
hasMany: [{ model: 'MyApp.model.Tyre' }]
}
});
\end{lstlisting}
Tutto ciò risulta di grande interesse perché ci permette di avvicinare sempre di più il nostro codice a quella che è la forma del modello logico del DB. Tuttavia dagli studi eseguiti emerge una mancanza significativa negli automatismi offerti da Sencha. Supponendo di creare uno store che si basa sulla memorizzazione di istanze di tyre, vogliamo definire una vista a cui associarlo, e in cui visualizzare direttamente non solo i dati dei pneumatici, ma anche i dati contenuti nei modelli associati, che per l'appunto, si basano su di un proxy di tipo JsonP (per ulteriori informazioni vedi "Approfondimento sui proxy"). Per esempio vogliamo che ogni item della lista visualizzi il nome del pneumatico, l'id della macchina associata e il nome del proprietario della macchina. Tutto ciò risulta essere problematico.
\begin{description}
\item[Il problema]: analizzando in dettaglio il problema osserviamo quanto segue. Quando viene eseguito il comando load dello store, questi carica i dati dal server remoto usando le informazioni di configurazione del proxy. In molti casi però il caricamento dei dati è asincrono. Questo significa che i record dei pneumatici e delle auto saranno istanziati senza conoscere le loro controparti correlate. Così, quando abbiamo recuperare un record Tyre dallo store, questi non ha alcun riferimento al record (padre) auto, che vorremo utilizzare nel componente List. L'unico modo per farlo è quello di caricare l'intera struttura dati dei proprietari (owner), compresi i "figli" di car e di tyre, in un singolo passaggio e utilizzando una struttura dati "ibrida". In pratica questo metodo di caricamento dei dati è ingombrante e per lo più non supportato dalle tipiche backend's API.
\item[La soluzione]: una soluzione al problema è stata trovata da due sviluppatori: Rob Boerman e Aaron Smith. Questi l'hanno pubblicata nel proprio sito web, raggiungibile all'indirizzo riportato al termine di questo paragrafo. Essa consiste nel ridefinire un modello "base" che provvede all'inizializzazione delle associazioni (agli altri modelli), quando una componente (per esempio una lista) necessita dei dati. Ciò si traduce nell'istruire il modello su come rintracciare i dati associati.
Osservando che uno dei metodi principali per ottenere dati a partire da uno store è il metodo getData(), gli sviluppatori sopramenzionati hanno optato per un ovverride del metodo stesso, che prevedesse per l'appunto le informazioni su come ottenere i dati associati. Quindi ogni modello del sistema sarà creato come estensione di questo modello base.
\end{description}
Poiché una trattazione estesa di questo argomento, esula dagli scopi del documento, si rimanda al seguente link per ogni approfondimento:
\url{http://appointsolutions.com/2012/07/using-model-associations-in-sencha-touch-2-and-ext-js-4/}
Come si potrà osservare, quanto qui riportato è un piccolo estrapolato dell'articolo.
Prendendo in considerazione altre soluzioni, più facile da pensare ma decisamente più onerosa in termini di tempo di elaborazione, si osserva la possibilità di definire ad hoc delle richieste Ajax al server per ottenere tutti i dati necessari.
\subsubsection{FireEvent: una gestione intelligente degli eventi}
In un ottica di programmazione basata su pattern architetturale MVC, la vista è svincolata dal conoscere le regole di business attuate per modificare i dati in relazione al verificarsi di un determinato evento. Infatti dovrebbe essere compito del controller mettersi in "ascolto", su delle view di cui è responsabile, ed essere pronto a catturare gli eventi sollevati dai componenti delle view. Quindi il controller potrà associare agli eventi il giusto \textit{handler}, per la risoluzione del medesimo.
Sencha permette di gestire tale procedura nel seguente modo:
\begin{itemize}
\item[1)] si definisce un controller (in generale diciamo uno per ogni view), e si dichiarano le viste su cui dovrà mettersi in ascolto.
\item[2)] quindi per ogni evento particolare, legato all'interazione dell'utente con un componente della view, si definisce una "delega" in un listener della vista (ciò avviene nella view stessa) il cui compito sarà delegare l'evento ad un \textit{handler} appropriato. la funzione \textit{handler} avrà poi il compito di sollevare un nuovo evento (convenzionalmente nominabile come ''comamand'') mediante l'evento FireEvent. Si osservi che in questa ottica la view non conosce direttamente chi la osserva, il che rende il codice molto più mantenibile. Analoga conclusione vale per il fatto che cosi facendo vi è una completa divisione tra logica e grafica.
\item[3)] a questo punto di può tornare sul controller e dichiarare che la view x, su cui il controller è in ascolto, può sollevare l'evento y e che tale situazione si risolve delegando la gestione dell'evento ad una funzione appositamente creata. In linea generale se l'operazione riguarda la manipolazione di alcuni dati sensibili, ciò dovrebbe avvenire richiamando i metodi definiti nel model.
\end{itemize}
Per completezza riportiamo di seguito un esempio:\\
quella che segue è la vista, si osservi che
\begin{itemize}
\item[A)] è presente un listener che definisce le associazioni evento-componente-handler;
\item[B)] è presente un \textit{handler} associato all'evento tap del bottone;
\item[C)] l'\textit{handler} non fa altro che sollevare un nuovo evento tramite metodo FireEvent.
\end{itemize}
\lstset{language=java,caption={Gestione degli eventi - sollevare un evento con FireEvent},label=DescriptiveLabel}
\begin{lstlisting}
//Vista NotesList -> rappresenta una lista di note. contiene un bottone NewNote il cui scopo consiste nell'effettuare una transizione verso la vista NoteEditor
Ext.define('NotesApp.view.NotesList',
{
extend: 'Ext.Container',
requires: ['Ext.dataview.List'],
alias: 'widget.notesListView',
config:
{
...
items:
[
{
xtype: 'toolbar',
docked: 'bottom',
items:
[
{ xtype: 'spacer' },
{
xtype: 'button',
itemId: 'newButton',
text: 'New Note',
ui: 'action'
},
{ xtype: 'spacer' }
]
},
...
]
...
//Listeners: usato per mettere la vista in ascolto di se stessa. L'idea consiste nel catturare gli eventi del tipo "iterazione dell'utente con le componenti della view"
listeners:
[
{
//componente da "ascoltare" identificato mediante id
delegate: '#newButton',
//tipologia di evento da "catturare"
event: 'tap',
//funzione "handler" per la risoluzione dell'evento
fn: 'onNewButtonTap'
},
...
]
},
//---------------FireEvent-------------------
//Handler di #newButton - evento: tap
onNewButtonTap: function()
{
console.log('newNoteCommand');
//Sollevo l'evento newNoteCommand
this.fireEvent('newNoteCommand', this);
},
...
});
\end{lstlisting}
Per completare l'esempio, riportiamo il codice commentato del controller. Qui vogliamo mettere in evidenza i seguenti punti:
\begin{itemize}
\item[A)] il controller definisce una lista refs delle viste su cui restare in ascolto;
\item[B)] il controller definisce, in una zona detta control, le coppie nome evento - command da richiamare;
\item[C)] al termine del listato compaiono i command da richiamare per processare la richiesa inoltrata in princio dall'iterazione utente - componente della vista;
\end{itemize}
\lstset{language=java,caption={Gestione degli eventi - sollevare un evento con FireEvent},label=DescriptiveLabel}
\begin{lstlisting}
//Controller Notes -> controller della vista
Ext.define('NotesApp.controller.Notes',
{
extend: 'Ext.app.Controller',
config:
{
//refs usato per tracciare i riferimenti nomeVista-aliasUsatoNelController
refs:
{
//stabilisco che questo controller osserva gli eventi lanciati dall'oggetto xtype = noteListView
notesListView: 'notesListView',
...
},
//parte di controller. Per ogni vista gestita dal controller, definisce le coppie nomeEventoDaCatturare-nomeHandlerDelController
control:
{
notesListView:
{
//definisco la lista degli eventi che possono essere lanciati (mediante fireEvent) dall'oggetto che sto osservanto (xtype: notesListView)
newNoteCommand: 'onNewNoteCommand',
...
/*this ref automatically creates a getNoteEditor() function in the controller, which we can use to refer to the NoteEditor instance and make it the active View in the application.*/
},
...
}
},
//---------LISTA DEI COMANDI---------------
//comando associato all'evento newNoteCommand dell'oggetto notesListView.
onNewNoteCommand: function()
{
//istruzioni per risolvere l'evento. Nel caso, si tratta di istruzioni per creare una nuova nota vuota, ed effettuare uno shift di navigazione, alla view contenente l'editor di note.
},
...
});
\end{lstlisting}
\subsubsection{Gestire il multi-tap}
Come si può vedere dall'esecuzione di una qualsiasi app di Sencha, questo sono realizzate in modo tale da non provocare un blocco dell'interfaccia in seguito all'esecuzione di un comando dovuto alla pressione di un bottone. Cosi facendo l'utente ha la possibilità di navigare nell'interfaccia, mentre in background vengono svolte le operazioni da lui richieste. Ciò è possibile mediante un sistema di gestione degli eventi. Infatti quando premiamo, o se preferite, quando eseguiamo un tap su di un Ext.Button, viene generato un evento che sarà catturato da un handler da noi specificato. Questo sistema rende quindi asincrona ogni richiesta da noi fatta al sistema per mezzo di una componente grafica.
A questo punto molti sviluppatori non si avvedono di un potenziale problema di stabilità dell'applicativo. Se supponiamo l'utente di turno, per un qualche motivo, inizia a premere ripetutamente il pulsante A, questo provocherà una serie di fireEvent relativi all'evento tap che, catturati dall'handler, provocheranno l'esecuzione del blocco di codice designato. A ciò si aggiunge un ulteriore problema: JavaScript non gestisce la mutua esclusione. Non esistono quindi istruzioni atomiche da usare come ''barriera di sincronizzazione''.
Considerando quanto detto, si pensi allo scenario: un bottone A ha come handler associato una funzione di download che:
\begin{itemize}
\item[1)] scarica i dati dal server;
\item[2)] cancella quelli locali;
\item[3)] salva i dati temporanei nello spazio di memoria locale.
\end{itemize}
In un tale contesto, se l'utente preme ripetutamente A, genererà diverse richieste di download. Un possibile risultato è che due processi eseguono in successione il passo 1) e il passo 2). A tal punto entrambi avrebbero cancellato due volte l'area di memoria locale, e si appresterebbero a sovrascriverci i dati dalla loro zona di memoria. Ciò provoca di conseguenza una ridondanza nei dati scaricati.
Purtroppo l'incapacità di JavaScript di eseguire blocchi di codice in mutua esclusione (si badi bene, semplici barriere di tipo if con variabili booleane non sono sufficiente) rende il nostro codice debole e potenzialmente pericoloso. Per fortuna, in seguito ad una domanda sul forum di Sencha, uno sviluppatore ha definito un Ext.Button in grado di accettare un unico tap in una serie di tap concatenati. Di seguito riportiamo il codice di tale componente.
\lstset{language=java,caption={org.s2.singleEventItem.button.SingleTapButton},label=DescriptiveLabel}
\begin{lstlisting}
Ext.define('org.s2.singleEventItem.button.SingleTapButton',
{
extend : 'Ext.Button',
xtype : 'stbutton',
initialize : function() {
var me = this;
me.element.on({
scope : me,
singletap : 'onSingleTap',
doubletap : 'onDoubleTap'
});
me.callParent();
},
onSingleTap : function () {
this.fireEvent('singletap', this);
},
onDoubleTap : function () {
this.fireEvent('doubletap', this);
}
});
\end{lstlisting}
Ora invece vediamo come usare tale componente. Per prima cosa creiamo nella nostra vista un bottone avente come xtype, quello del nostro SingleTapButton. Una tale operazione si fa, per esempio, nel seguente modo:
\lstset{language=java,caption={Usare il SingleTapButton - definire il bottone},label=DescriptiveLabel}
\begin{lstlisting}
...
{
xtype: 'stbutton', //ora il nostro bottone accettera' il single tap.
itemId: 'downloadButton',
iconCls: 'download',
iconMask: true,
ui: 'action',
scope: this
},
...
\end{lstlisting}
Passiamo quindi a definire l'handler da associare. Con il sistema appreso nella sezione del fireEvent, definiamo nella config della vista, un listener abilitato alla delega dell'evento singletap del nostro bottone:
\lstset{language=java,caption={Usare il SingleTapButton - listeners},label=DescriptiveLabel}
\begin{lstlisting}
listeners:
[
...
{
delegate: '#downloadButton',
event: 'singletap',
fn: 'onDownload'
},
...
]
\end{lstlisting}
A questo punto basta definire la funzione onDownload, dalla quale genereremo un evento che sarà catturato da un apposito controller. Nel controller si definirà la parte di logica per la gestione dell'evento.
Per prendere nota del dibattito presente sull'argomento nel forum di Sencha, si rimanda al link:\\
\url{http://www.sencha.com/forum/showthread.php?208657-Multiple-taps-issue-BUG}
\subsubsection{Migliorare le performance di Sencha}
Sencha si è rivelato un framework dalle grandi potenzialità. Purtroppo però, il suo utilizzo mediante PhoneGap genera app piuttosto pesanti e non ben ottimizzate. Dopo diverse prove infatti si è osservato che su smatphone di fascia media (ovviamente rispetto al periodo in cui è scritto il documento ), le applicazioni costruite con tali framework risultavano essere piuttosto lente. Invece l'esecuzione su tablet ha dato esiti soddisfacenti. Comunque per cercare di migliorare il più possibile le app e i sorgenti che le compongono si consiglia di adottare le best practice riportate all'indirizzo sottostante:\\
\url{http://dionbeetson.blogspot.it/2012/10/sencha-touch-performance-tips-and-tricks.html}
\subsection{Eseguire test di unità su componenti Sencha}
\subsubsection{Syntax check}
Oggi giorno per garantire un prodotto software di qualità è necessario testarlo in più modi, affinché si possa stabilire con certezza che il suo comportamento è corretto. Tra le varie tipologie di test, la prima che merita di essere citata, è sicuramente la syntax check. Questa tecnica, che ritroviamo eseguita automaticamente nei linguaggi compilati, al momento della compilazione, ci permette di rilevare errori sintattici nel codice. JavaScript non essendo un linguaggio che richiede compilazione, non offre la possibilità di individuare gli errori, se non eseguendo il codice stesso. Inoltre si osservi che a molti piccoli errori, è il browser stesso a porvi rimedio. Questo infatti è il caso dei '';'' presenti al termine delle istruzioni JavaScript.
Quindi per offrire allo sviluppatore uno strumento valido al fine di rilevare ogni genere di errore, uno sviluppatore della Sencha.inc ha ideato un programma eseguibile da shell, che data una directory principale e una lista di sotto-directory da escludere durante l'attività di check, si occupa di esaminare ogni file con estensione .js usando il tool JsLint (\url{http://www.jslint.com/}). Per un'approfondimento sul tema, comprese le istruzioni per mettere in pratica tale tecnica, rimandiamo al link \url{http://www.sencha.com/blog/automating-unit-tests}.
\subsubsection{Configurare Jasmine per testare Sencha}
Come prima cosa è importante configurare correttamente l'ambiente di lavoro. Iniziamo scaricando la versione stand-alone di Jasmine, all'indirizzo \url{https://github.com/pivotal/jasmine/downloads}. Quindi scompattiamo il contenuto del pacchetto .zip nella cartella /test/unitTest della directory principale del nostro progetto. Procediamo quindi seguendo il seguente iter:
\begin{itemize}
\item[1)] definiamo all'interno della cartella /test/unitTest una cartella spec, dove andremo a inserire i file .js che definiscono le test suit;
\item[2)] creiamo un file app-test.js che andremo a mettere nella stessa directory in cui si trova l'app.js della nostra applicazione;
\item[3)] infine andiamo a creare un file run-test.html.
\end{itemize}
A questo punto procediamo procediamo andando a scrivere il contenuto di app-test.js. Questo file rappresenta il file centrale per il caricamento delle componenti da testare e per l'avvio del kernel di Jasmine. App-test.js può essere scritto nel seguente modo:
\lstset{language=java,caption={Template di app-test.js},label=DescriptiveLabel}
\begin{lstlisting}
Ext.application({
//elenco di modelli e store necessari per il testing
//Per esempio:
models: ['Note','Author'],
stores: ['Notes'],
requires:
[
//elenco delle classi richieste per l'esecuzione del test.
//Per esempio:
'org.s2.syncEngine.SyncManager',
'org.s2.syncEngine.basicSyncStore.download.DownloadStoreFactory',
'org.s2.syncEngine.basicSyncStore.upload.CommitStoreFactory',
'org.s2.syncEngine.basicSyncStore.storeCommand.AddCommit',
'org.s2.syncEngine.basicSyncStore.storeCommand.UpdateCommit',
'org.s2.syncEngine.basicSyncStore.storeCommand.DeleteCommit',
'org.s2.syncEngine.basicSyncStore.SyncStore',
'NotesApp.model.Author',
],
//Per evitare la generazione del viewport
autoCreateViewport: false,
//nome dell'app
name: 'NotesApp',
//Lancher di app-test. Usare sempre questo template
launch: function()
{
jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
jasmine.getEnv().execute();
}
});
\end{lstlisting}
Quindi procediamo definendo il file run-test.html:
\lstset{language=java,caption={Esempio di run-test.html},label=DescriptiveLabel}
\begin{lstlisting}
<html>
<head>
<meta charset="UTF-8">
<!-- i commenti indicano cosa inserire nelle varie parti. I dati non commentati sono lasciati per esempio-->
<title>NotesApp unit test - Jasmine</title>
<!-- Jasmine css file: carica il css di Jasmine-->
<link rel="stylesheet" type="text/css" href="app-test/lib/jasmine-1.3.1/jasmine.css">
<!-- <x-bootstrap> inseirisci qui la libreria sencha-->
<script src="lib/ST2/sencha-touch-all-debug.js"></script>
<!-- Jasmine js files -->
<script type="text/javascript" src="app-test/lib/jasmine-1.3.1/jasmine.js"></script>
<script type="text/javascript" src="app-test/lib/jasmine-1.3.1/jasmine-html.js"></script>
<!-- App css files -->
<link rel="stylesheet" href="./lib/ST2/resources/css/sencha-touch.css" type="text/css">
<link rel="stylesheet" href="./resources/css/app.css" type="text/css">
<!-- application source files -->
<script src="app-test.js"></script>
<!-- / application source files: inserisci qui gli spec per il testing delle singole componenti -->
<script type="text/javascript" src="app-test/spec/IndexIDStoreSpec.js"></script>
<script type="text/javascript" src="app-test/spec/IndexIDStoreFactorySpec.js"></script>
<script type="text/javascript" src="app-test/spec/DownloadStoreFactorySpec.js"></script>
<script type="text/javascript" src="app-test/spec/CommitStoreFactorySpec.js"></script>
<script type="text/javascript" src="app-test/spec/AddCommitSpec.js"></script>
<script type="text/javascript" src="app-test/spec/UpdateCommitSpec.js"></script>
<script type="text/javascript" src="app-test/spec/DeleteCommitSpec.js"></script>
<script type="text/javascript" src="app-test/spec/SyncStoreSpec.js"></script>
<script type="text/javascript" src="app-test/spec/SyncManagerSpec.js"></script>
</head>
<body></body>
</html>
\end{lstlisting}
Ora l'ambiente è configurato. Non dobbiamo fare altro che costruire le nostre spec e metterle nell'omonima cartella definita al punto 1. Per eseguire i test basta richiamare dal browser all'indirizzo localhost il file run-test.js.
\subsubsection{Unit test - intoduzione}
La seconda tipologia di test, più interessante e atta a rilevare comportamenti anomali nelle componenti che costituiscono l'applicativo, prende il nome di \textbf{unit test}. Questa tecnica si basa sulla seguente considerazione: quando scriviamo del codice, lo facciamo sapendo già quali modifiche esso apporterà al sistema nei vari casi di esecuzione. Quindi il blocco di codice da testare avrà un suo \emph{valore atteso} che non necessariamente sarà uguale al suo \emph{valore effettivo}, ossia quello che rileviamo in fase di esecuzione. Tale anomalia è dovuta ad un nostro errore. I test di unità quindi, basandosi su un valore atteso (da noi specificato), eseguono il blocco di codice da testare e al termine verificano, con una certa tipologia di comparazione (uguale a, diverso da, maggiore di, ecc...) da noi specificata, se il valore ottenuto è conforme a quello atteso.
Creare ed eseguire test di unità per sorgenti JavaScript è un'operazione avviabile con diversi tool. Per la sua semplicità, per la chiarezza di linguaggio e per le potenzialità offerte (e.g. facilitazioni nel test di blocchi asincroni) questa guida si rifarà al tool Jasmine. Poiché una trattazione approfondita sulle basi di Jasmine esula dallo scopo di questa sezione si rimanda il lettore all'appendice C, dove potrà trovare un mini-tutorial e dei link utili. Di seguito parleremo delle best practice da adottare nel testing si Sencha, dando per scontata la conoscenza di Jasmine.
\subsubsection{Best practice per il testing degli store}
Quando testiamo uno store è sempre molto utile definire una funzione per creare lo store, e tramite le apposite funzioni beforEach e afterEach, provvedere alla sua creazione (All'inizio di ogni singolo test) e alla sua eliminazione (alla fine del test). Per rendere meglio questo concetto proponiamo il seguente template:
\lstset{language=java,caption={Template per il testing degli store},label=DescriptiveLabel}
\begin{lstlisting}
describe("NomeStoreSpec", function()
{
//classe contenente che definisce lo store
var storeCreator = function()
{
var myoStore = Ext.create('AliasStore');
return myoStore;
};
//istanza di store usata per i test
var store = null;
beforeEach(function()
{
//ad ogni test creo lo store
if (!store)
{
store = storeCreator();
store.load();
}
expect(store).toBeTruthy();
waitsFor(function(){ return !store.isLoading(); },"load never completed", 4000);
});
afterEach(function()
{
store.getProxy().dropTable();
store = null;
});
...
//elenco di it() ...
});
\end{lstlisting}
Per quanto riguarda invece i test da eseguire, si raccomanda di assicurarsi come prima che lo store sia stato caricato con successo. Quindi si procede verificando che il modello utilizzato dallo store corrisponda a quello che si è assegnato. Ciò potrebbe sembrare banale, ma è comunque un test da fare, specie per gli store più elaborati da noi ridefiniti. Analogamente è importante osservare che il proxy usato dallo store sia del tipo atteso. Proponiamo di seguito un esempio di come possono essere riportati due it del genere:
\lstset{language=java,caption={Template per il testing degli store - it di base},label=DescriptiveLabel}
\begin{lstlisting}
it('IndexIDStore caricato con successo', function()
{
var isLoaded = store.isLoaded();
expect(isLoaded).toBe(true);
});
it("Model dello store valido (type: NOME_MODELLO)",function()
{
var modelloStore = store.getModel().getName();
var modelloAtteso = 'PATH_COMPLETA_DEL_MODELLO';
//e.g PATH_COMPLETA_DEL_MODELLO = 'org.s2.MioStore.MioModello'
expect(modelloStore).toBe(modelloAtteso);
});
it("Proxy type di store valido (type: TIPO_ATTESO)",function()
{
var proxyTypeStore = store.getProxy().config.type;
var proxyTypeAtteso = 'TIPO_ATTESO';
//e.g TIPO_ATTESO = 'sql'
expect(proxyTypeStore).toBe(proxyTypeAtteso);
});
\end{lstlisting}
\subsubsection{Approfondimenti}
Per ulteriori approfondimenti sul tema, si rimanda ai seguneti link:
\begin{itemize}
\item \textbf{Tutorial del Pivotal Lab}: guida completa che spiega come eseguire test di unità su codice Sencha. I link per le varie parti sono:\\
\url{http://pivotallabs.com/sencha-touch-bdd-part-1/}\\
\url{http://pivotallabs.com/sencha-touch-bdd-part-2/}\\
\url{http://pivotallabs.com/sencha-touch-bdd-part-3/}\\
\url{http://pivotallabs.com/sencha-touch-bdd-part-4/}\\
\url{http://pivotallabs.com/sencha-touch-bdd-part-5/}
\item \textbf{Approfondimento sui test di regressione}:\\
\href{http://blogs.walkingtree.in/2013/04/09/...}{http://blogs.walkingtree.in/2013/04/09/unit-and-regression-test-automation-of-sencha-applications/}
\item \textbf{Approfondimento sugli UI test (test d'integrazione)}:\\
\url{https://www.sencha.com/blog/ui-testing-a-sencha-app}
\end{itemize}
\clearpage
\section{Usare PhoneGap sviluppando con Sencha}
\subsection{La mia prima applicazione}
Come già accennato in precedenza, questo manuale ha lo scopo di definire delle linee guida per l'uso di PhoneGap in associazione al framework Sencha Touch 2. Come primo passo si raccomanda di seguire la guida presente all'indirizzo riportato al termine di questo paragrafo. Essa vi guiderà, in un percorso step-by-step, nella creazione della vostra prima applicazione: un editor di note. Il tutorial mette in evidenza:
\begin{itemize}
\item struttura dell'architettura di Sencha e come si debba operare per far collaborare i model, le view, gli store ed i controller;
\item panoramica generale sulle proprietà degli oggetti più usati;
\item procedure da seguire per l'archiviazione dei dati attraverso l'uso di LocalStorage (per comprendere cosa sia un WebStorage si rimanda alla sottosezione ''Panoramica generale degli store HTML5'');
\item creazione di un sistema di gestione degli eventi.
\end{itemize}
Link al tutorial:\\
\url{http://miamicoder.com/2012/how-to-create-a-sencha-touch-2-app-part-1/}\\
\subsection{Sistema di archiviazione dati}
\subsubsection{Panoramica generale degli store HTML5}
PhoneGap permette alle nostre applicazioni di operare un archiviazione dei dati usufruendo delle potenzialità dell'HTML5. Quest'ultimo definisce 4 tipologie di \texttt{store}:
\begin{itemize}
\item \textbf{Local Storage};
\item \textbf{Session Storage};
\item \textbf{WebSQL Storage};
\item \textbf{IndexedDB Storage};
\end{itemize}
Le tecnologie Local Storage e Session Storage vengono in genere denominate Web Storage e rappresentano una comune interfaccia di programmazione per le pagine Web. Queste funzionalità costituiscono un miglioramento dei cookie e sono di dimensioni limitate, che variano da 5 a 10 MB a seconda del browser utilizzato. Non inviano i dati al server, ma archiviano i dati sui computer client locali. \textbf{Local Storage è un'archiviazione persistente, mentre Session Storage offre un'archiviazione temporanea}.
In merito al Session Storage è importante ricordare che i dati cosi archiviati esistono solo nel contesto in cui vengono creati. Tale contesto non si basa solo sul sito remoto (l'URL di navigazione) ma anche in base alla tab o alla finestra utilizzata dall'utente.
Il Local Storage invece è un oggetto specifico del browser, condiviso da tutte le istanze (finestre o tab del browser) aperte dall'utente.
Più interessante, il WebSQL fornisce un sistema di archiviazione attraverso la creazione di un mini database in SQLite 3 (per maggiori informazioni su SQLite vedere Appendice A). Durante lo studio effettuato su tale argomento è però emersa una \textit{"discrepanza"} tra quanto riportato da libri/siti/forum e quanto effettivamente si può fare nella realtà. La documentazione riporta che, analogamente ai sistemi di storage già visti, anche il WebSQL risulta avere un limite di default di 5MB, ma è possibile aumentarlo a piacere impostando il campo size. Tuttavia contrariamente a quanto riportato nella documentazione reperita, è stato possibile sforare questo limite senza alcun problema. Anche la ridefinizione del campo size di un database, abbassandolo da 5 a 2 MB, non ha comportato problemi di sorta permettendoci di creare database di dimensione superiore. Uno studio ulteriore ha comportato l'individuazione di un sito dove si accennava ad una caratteristica del browser Safari. Questo sembrerebbe essere l'unico ad adottare una vera limitazione sull'uso dei web database. Esso infatti dovrebbe richiedere all'utente, al momento del superamento dei 5 MB, l'autorizzazione ad espandere la sua dimensione di altri 5 MB, e cosi man mano che si sfora nel size attuale. Considerazione in merito ai test apportati (in conferma a quanto qui detto) sono reperibili nell'appendice B.
Tornando a parlare del WebSQL in sé, si sa inoltre che è stato deprecato dal W3C in favore dell'IndexedDB. Quest'ultimo però non è ancora supportato da tutti i browser mobile (come evidenziato al sito \url{http://caniuse.com/#feat=indexeddb}).
Per maggiori informazioni sui sistemi di archiviazione di HTML5 è possibile consultare le informazioni riportate al seguente link: \url{http://software.intel.com/it-it/articles/html5-local-storage}.
\subsubsection{Utilizzare Web Storage e WebSQL con Sencha}
Sencha Touch 2 implementa già delle funzionalità per rendere il più automatico possibile l'uso dei Web Storage. In linea generale la strategia consiste nel creare un Ext.data.Store facente da ponte tra le viste e l'oggetto contenente i dati (proxy), ed un Ext.data.Model che definisce la struttura di quest'ultimi. Quando vogliamo creare un sistema di archiviazione con Sencha, dobbiamo prima decidere che tipo di Web Storage usare. La scelta dello store comporta l'uso di un determinato oggetto proxy:
\begin{itemize}
\item Local Storage -> Ext.data.proxy.LocalStorage;
\item Session Storage -> Ext.data.proxy.SessionStorage;
\item WebSQL Storage -> Ext.data.proxy.Sql;
\item IndexedDB Storage -> Non implementato da Sencha, poiché tale sistema di archiviazione non è ancora supportato da diversi mobile browser.
\end{itemize}
Si osservi che l'uso dei proxy si basa su di un interfaccia comune, il che rende pratico e veloce il passaggio da un tipo all'altro. In fase di developing si sottolinea l'utilità degli strumenti per sviluppatori presenti su Google Chrome. Andando alla voce risorse sarà possibile esaminare il contenuto dei diversi tipi di storage.
Una trattazione più approfondita di questo argomento esula dallo scopo del documento, pertanto si rimanda a link esterni:
\begin{itemize}
\item per un approfondimento sull'uso del Ext.data.proxy.LocalStorage rimandiamo alla guida della sezione ''La mia prima applicazione'';
\item per imparare ad usare Ext.data.proxy.Sql si rimanda al link: \url{http://druckit.wordpress.com/2013/04/02/revisiting-the-sencha-touch-2-2-sql-proxy/}.
\end{itemize}
\subsubsection{Considerazioni sull'uso dei Web Storage}
Per i nostri scopi (fornire un sistema di archiviazione dati su dispositivi mobile), i Web Storage presentano diversi problemi:
\begin{itemize}
\item lo spazio di memorizzazione è limitato;