L'app mobile della Cittadinanza Digitale
FAQ
Cos'è la Cittadinanza Digitale?
La Cittadinanza Digitale ha l'obiettivo di portare il cittadino al centro dei servizi erogati dalle pubbliche amministrazioni italiane.
Il progetto si estende su due linee:
- la costruzione di una piattaforma di componenti che abilitano lo sviluppo di servizi digitali incentrati sul cittadino
- un'interfaccia del cittadino per gestire i propri dati e il proprio profilo di cittadino digitale
Cos'è l'app mobile della Cittadinanza Digitale?
L'app mobile della Cittadinanza Digitale è un'applicazione mobile nativa per iOS e Android con duplice scopo:
- essere un interfaccia per il cittadino verso i propri dati e il proprio profilo di cittadino digitale
- fungere da reference implementation delle integrazioni con la piattaforma di Cittadinanza Digitale
Chi sviluppa l'app?
Lo sviluppo dell'app è portato avanti da diversi contributors: L'Agenzia per l'Italia Digitale, il Team per la Trasformazione Digitale e volontari indipendenti che credono nel progetto.
Posso usare l'app?
Per ora l'app non è ancora stata pubblicata sugli app store per cui non è possibile installarla tramite i meccanismi abituali.
Se sei uno sviluppatore puoi invece compilare l'app sul tuo computer e installarla manualmente sul tuo device.
Quando sarà pubblicata l'app?
Quando l'app avrà raggiunto un livello di qualità e di utilità che ci soddisfa, la renderemo disponibile a tutti i cittadini.
Come posso darvi una mano?
Segnalazione di bug, bugfix ed in genere qualsiasi miglioramento è il benvenuto! Mandaci una Pull Request!
Se invece hai un po' di tempo da dedicarci e vuoi essere coinvolto in modo continuativo, mandaci una mail.
Tecnologie usate
Architettura
Autenticazione SPID
L'applicazione si appoggia ad un backend web per l'autenticazione a SPID. Il backend implementa un Service Provider SAML2 (tramite Shibboleth) che si occupa dell'autenticazione dell'utente sugli Identity Provider SPID.
L'autenticazione tra l'applicazione e il backend avviene tramite un token di sessione, generato dal backend al momento dell'autenticazione sull'IdP SPID.
Una volta che il backend comunica il token di sessione all'applicazione, questo viene usato per tutte le successive chiamate che l'applicazione fa alle API esposte dal backend.
Il flusso di autenticazione è il seguente:
- L'utente seleziona l'IdP
- L'app apre una webview sull'endpoint di autenticazione Shibboleth implementato nel backend, specificando l'entity ID dell'IdP selezionato dall'utente e, come URL di ritorno, l'URL dell'endpoint che genera un nuovo token di sessione (es.
/app/token/new
). - Shibboleth prende in carico il processo di autenticazione verso l'IdP
- Ad autenticazione avvenuta, viene fatto un redirect dall'IdP all'endpoint del backend che si occupa della generazione di un nuovo token di sessione.
- L'endpoint di generazione di un nuovo token riceve via header HTTP gli attributi SPID, poi genera un nuovo token di sessione (es.
123456789
) e restituisce alla webview un HTTP redirect verso un URL well known, contenente il token di sessione (es./api/token/123456789
) - L'app, che controlla la webview, intercetta questo URL prima che venga effettuata la richiesta HTTP, ne estrae il token di sessione e termina il flusso di autenticazione chiudendo la webview.
Successivamente il token di sessione viene usato dall'app per effettuare le chiamate all'API di backend (es. per ottenere gli attributi SPID).
Come contribuire
Pre-requisiti
nodenv
Su macOS e Linux si consiglia l'uso di nodenv per la gestione di versione multiple di NodeJS.
La versione di node usata nel progetto è questa.
Se si ha già nodenv
installato e configurato sul proprio sistema, la versione
di node
corretta verrà impostata quando si accede alla directory dell'app.
yarn
Per la gestione delle dipendenze javascript usiamo Yarn.
rbenv
Su macOS e Linux si consiglia l'uso di rbenv per la gestione di versione multiple di Ruby.
La versione di Ruby usata nel progetto è questa.
Se si ha già rbenv
installato e configurato sul proprio sistema, la versione
di Ruby corretta verrà impostata quando si accede alla directory dell'app.
Bundler
Alcune dipendenze (es. CocoaPods) sono installate tramite bundler.
React Native
Seguire il tutorial (Building Projects with Native Code) per il proprio sistema operativo.
Se si dispone di un sistema macOS è possibile seguire sia il tutorial per iOS che per Android. Se invece si dispone di un sistema Linux o Windows sarà possibile installare solo l'ambiente di sviluppo per Android.
Compilazione e lancio sul simulatore
Dipendenze
Per prima cosa installiamo le librerie usate dal progetto:
$ bundle install
$ yarn install
$ cd ios
$ pod install
Generazione definizioni API
Il secondo passo è generare le definizioni dalle specifiche openapi:
$ yarn generate:api-definitions
Configurazione dell'app
Infine copiamo la configurazione di esempio per l'app.
$ cp .env.example .env
Nota: la configurazione di esempio imposta l'app per interfacciarsi al nostro ambiente di test, su cui lavoriamo continuamente - potrebbe quindi accadere che alcune funzionalità non siano sempre disponibili o completamente funzionanti.
Installazione sul simulatore
Su Android (il simulatore del device va lanciato a mano):
$ react-native run-android
Su iOS (il simulatore verrà lanciato automaticamente):
$ react-native run-ios
Nota: l'app utilizza CocoaPods, il progetto da eseguire è quindi ItaliaApp.xcworkspace
anzichè ItaliaApp.xcodeproj
(run-ios
lo rileva automaticamente)
Compilazione (release)
Per il rilascio dell'app sugli store usiamo Fastlane.
iOS
La distribuzione della beta viene fatta con il modello ad-hoc.
Per rilasciare una nuova beta:
$ bundle exec fastlane beta
Per aggiungere un nuovo device alla distribuzione ad-hoc:
$ bundle exec fastlane register_new_device
Android
Per rilasciare una nuova alpha:
$ bundle exec fastlane alpha
Installazione su device
iOS
react-native run-ios --configuration Release --device 'YOUR_DEVICE_NAME'
Sviluppo con App Backend e IDP di test in locale
Per sviluppare l'applicazione utilizzando in locale l'App Backend e un IDP di test, è necessario seguire alcuni step aggiuntivi descritti di seguito.
Installazione di App Backend e IDP di test
Seguire la documentazione del repository italia-backend.
WebView, HTTPS e certificati autofirmati
Allo stato attuale react-native non consente di aprire WebView su url HTTPS con certificato autofirmato. L'IDP di test però fa utilizzo di HTTPS e di un certificato autofirmato. Per ovviare a questo problema è possibile installare in locale un Proxy che faccia da proxy-pass verso l'App Backend e l'IDP.
Installazione di mitmproxy
Un proxy semplice da utilizzare e addatto al nostro scopo è mitmproxy. Per l'installazione seguire la pagina di documentazione del sito ufficiale.
All'interno del repository è presente il file scripts/mitmproxy_metro_bundler.py
che permette al proxy di intercettare le richieste verso il Simulatore e, solo in caso di porte specifiche, effettuare il proxy-pass verso localhost.
Avviare il Proxy con il seguente comando:
SIMULATOR_HOST_IP=XXXXX mitmweb --listen-port 9060 --web-port 9061 --ssl-insecure -s scripts/mitmproxy_metro_bundler.py
Inserire al posto di XXXXX
:
10.0.2.2
(Standard Android Emulator)10.0.3.2
(Genymotion Android Emulator)
Installazione del certificato di mitmproxy all'interno dell'emulatore Android
Installare il certificato di mitmproxy all'interno dell'emulatore seguendo la giuda ufficiale.
Impostare il proxy per la connessione nell'emulatore Android
Nella configurazione della connessione inserire:
- IP Proxy:
10.0.2.2
(o10.0.3.2
nel caso si utilizzi Genymotion) - Porta Proxy:
9060
Aggiornare icone dell'applicazione
Vedere questo tutorial.
Internazionalizzazione
L'applicazione utilizza react-native-i18n per il supporto multilingua.
Per aggiungere una nuova lingua è necessario:
- Creare un nuovo file all'interno della directory
locales
usando come nome<langcode>.json
(Es:es.json
) - Copiare il contenuto di uno degli altri file
.json
già presenti - Procedere con la traduzione
- Modificare il file
ts/i18n.ts
aggiungendo tra gli import e nella variabileI18n.translations
la nuova lingua
Gestione degli errori
L'applicazione utilizza un custom handler per intercettare e notificare errori javascript causati da eccezioni non gestite. Il codice del custom handler è visibile nel file ts/utils/configureErrorHandler.ts
Monitoring della connessione
L'applicazione utilizza la libreria react-native-offline per monitorare lo stato della connessione. In caso di assenza di connessione viene visualizzata una barra che notifica l'utente. Lo stato della connessione è mantenuto all'interno dello store nella variabile state.network.isConnected
, è possibile utilizzare questo dato per disabilitare alcune funzioni durante l'assenza della connessione.
Fonts
L'applicazione utilizza il font Titillium Web
. I fonts vengono gestiti in modo differente da Android e iOS. Per utilizzare il font TitilliumWeb-SemiBoldItalic
ad esempio è necessario applicare le seguenti proprietà per Android:
{
fontFamily: 'TitilliumWeb-SemiBoldItalic'
}
mentre in iOS il codice da applicare è:
{
fontFamily: 'Titillium Web',
fontWeight: '600',
fontStyle: 'italic'
}
Per rendere la gestione dei font e delle varianti più sempice sono state create delle funzioni di utilità all'interno del file ts/theme/fonts.ts
Theming
L'applicazione utilizza native-base e i suo componenti per la realizzazione dell'interfaccia grafica. In particolare è stato scelto di utilizzare come base il tema material
previsto dalla libreria. Sebbene native-base permetta attraverso l'uso di variabili di personalizzare parte del tema è stato comunque necessario implementare delle funzioni ad-hoc che consentano di andare a modificare il tema dei singoli componenti.
Estensione di native-base
Nella directory /ts/theme
sono presenti alcuni file che consentono di gestire il tema in modo più flessibile rispetto a quanto permesso nativamente da native-base.
Variabili
Per definire nuove variabili da utilizzare nel tema dei componenti è necessario modificare il file /ts/theme/variables.ts
. Tale file si occupa di importare le variabili di base definite dal tema material
di native-base e permette di sovrascrivere/definire il valore di nuove variabili.
Tema dei Componenti
La libreria native-base definisce il tema di ogni singolo componente in un file .ts separato che ha come nome quello dello specifico componente. Ad esempio il file del tema relativo al componente Button
ha come nome Button.ts
.
Per ridefinire il tema dei componenti di native-base è necesario creare/modificare i file presenti nella directory /ts/theme/components
. Ogni file presente in questa directory deve esportare un oggetto che definisce il tema del componente. Prendiamo come esempio il file Content.ts
:
import { type Theme } from '../types'
import variables from '../variables'
export default (): Theme => {
const theme = {
padding: variables.contentPadding,
backgroundColor: variables.contentBackground
}
return theme
}
In questo file è possibile notare come vengono ridefiniti due attributi (padding
e backgroundColor
) utilizzando come valori quanto presente nelle relative variabili. L'oggetto restituito sarà utilizzato nel file /ts/theme/index.ts
per associarlo ad uno specifico tipo di componente (in questo caso NativeBase.Component
).
Un esempio più complesso permette di utilizzare le funzioni avanzate del layer di theming di native-base.
import { type Theme } from '../types'
import variables from '../variables'
export default (): Theme => {
const theme = {
'.spacer': {
'.large': {
height: variables.spacerLargeHeight
},
height: variables.spacerHeight
},
'.footer': {
paddingTop: variables.footerPaddingTop,
paddingLeft: variables.footerPaddingLeft,
paddingBottom: variables.footerPaddingBottom,
paddingRight: variables.footerPaddingRight,
backgroundColor: variables.footerBackground,
borderTopWidth: variables.footerShadowWidth,
borderColor: variables.footerShadowColor
}
}
return theme
}
All'interno del file del tema di un singolo componente è possibile infatti definire degli attributi specifici che verranno utilizzati solo nel caso in cui il componente in questione abbia una specifica proprietà. Definendo nell'oggetto di tema qualcosa come:
'.footer': {
paddingTop: variables.footerPaddingTop
}
se necessario, sarà possibile utilizzare il componente associandogli la proprietà footer
nel seguente modo <Component footer />
ed automaticamente il sistema di theming applicherà al componente gli attributi definiti (paddingTop: variables.footerPaddingTop
).
Altra funzione avanzata è quella che permette di definire il tema dei componenti figli a partire dal componente padre. Prediamo come esempio il seguente frammento di codice di un generico componente:
...
render() {
return(
<Content>
<Button>
<Text>My button</Text>
</Button>
</Content>
)
}
...
La libreria native-base permette di definire l'aspetto del componente figlio Text
presente all'interno del componente padre Button
. Ad esempio per definire la dimensione del testo in tutti i bottoni presenti nell'applicazione, è sufficiente inserire il seguente codice all'interno del file /ts/theme/components/Button.ts
:
import variables from '../variables'
export default (): Theme => {
const theme = {
'NativeBase.Text': {
fontSize: variables.btnTextFontSize
}
}
return theme
}
È possibile spingersi ancora oltre e combinare le due funzionalità viste in precedenza:
import variables from '../variables'
export default (): Theme => {
const theme = {
'.small': {
'NativeBase.Text': {
fontSize: variables.btnTextFontSize
}
}
}
return theme
}
In questo caso quanto definito all'interno dell'attributo NativeBase.Text
sarà utilizzato solo nel caso in cui il bottone abbia associata una proprietà dal nome small
.
Custom UI components
TextWithIcon
Un semplice wrapper in cui è possibile inserire un'icona ed un testo che verranno renderizzati uno di fianco all'altro.
Esempio di utilizzo:
<TextWithIcon danger>
<Icon name={'cross'} />
<Text>{I18n.t('onboarding.pin.confirmInvalid')}</Text>
</TextWithIcon>
Per cambiare il tema del wrapper, dell'icona o del testo modificare il file ts/theme/components/TextWithIcon.ts
.