UIApplication, la classe AppDelegate e la sua istanza a runtime

Una delle peculiarita' piu' importanti da tenere presente quando si programma per iPhone e' l'utilizzo della classe AppDelegate e la sua istanza in memoria. Ogni applicazione per iPhone gira in un ambiente cosidetto sandboxed. In pratica, ha un accesso limitato alle risorse di sistema ed una visione "inscatolata", appunto, dell'ambiente. In questo contesto, prende posto l'istanza della classe AppDelegate; quando si avvia un'applicazione dall'ambiente SpringBoard, quello che succede e' che viene lanciata la funzione main del nostro binario che provvede ad avviare il ciclo di mainloop contenente le inizializzazioni per:

- Il gestore di memoria
- Il gestore dell'ambiente grafico
- Il gestore d'eventi
- L'istanza della classe AppDelegate

Il nome della classe da utilizzare come AppDelegate viene passata come parametro della funzione:

int retVal = UIApplicationMain(argc, argv, nil, @"CoreAnimationAppDelegate");




UIApplication risulta essere fondamentale per poter referenziare il delegato principale (l'istanza di AppDelegate) della nostra applicazione caricata in memoria. La classe UIApplication, infatti, funziona da punto di coordinamento centrale. Ogni applicazione avra' esattamente una istanza di UIApplication, caricata in fase di startup dalla funzione UIApplicationMain vista sopra. Unitamente ad altri compiti, questa funzione avvia un'oggetto singleton di tipo UIApplication e, contestualmente, crea un'istanza della nostra classe delegato AppDelegate.

Il ruolo fondamentale di UIApplication e' quello di gestire lo smistamento e consegna degli eventi proveniente dall'utente. L'oggetto UIControl, visto nelle precedenti lezioni, invia tutti i messaggi relativi ad eventi dell'utente all'istanza di UIApplication. Sara' quest'ultima a girarli ai rispettivi destinatari durante l'esecuzione del mainloop della nostra applicazione.

UIApplication mantiene anche una lista di tutte le finestre (UIWindow) attualmente presenti nell'applicazione, in maniera da poter accedere ad ognuno degli oggetti UIView presenti in esse.

Abbiamo gia' parlato diverse volte dell'oggetto UIWindow e degli oggetti ad esso legati (UIScreen, UIViewController, UIView, ...). UIWindow ci permette di ottenere accesso ad una serie di funzioni e caratteristiche strettamente legate alle proprieta' della "finestra" principale della nostra applicazione. Le sue principali funzioni sono: fornire un'area per mostrare a schermo "qualcosa" e distribuire eventi ai sotto oggetti (dispatcher).

Insieme all'oggetto UIWindow, viene caricato in memoria un'istanza di un altro oggetto molto importante: UIScreen. Esso si preoccupa di fornire informazioni legate all'area fisica di schermo disponibile per le nostre applicazioni. Cosi', ad esempio, se volessimo conoscere l'area di schermo disponibile in un determinato istante, ci bastera' fare accesso all'istanza di UIScreen allocata per noi dall'application launcher di iPhone.

[[UIScreen mainScreen] bounds]


Quello che viene ritornato dal messaggio sopra e' un oggetto di tipo CGRect che potra' essere usato per svariati scopi come, ad esempio, istruire la dimensione di una UIView all'interno della nostra finestra principale.

La parte piu' interessante dei compiti di UIApplication, riguarda l'assegnazione di un delegato alla classe fornita come paramentro nel main. La classe scelta deve adottare il protocollo UIApplicationDelegate e rispondere (implementare) ad una serie di messaggi (metodi) necessari al corretto funzionamento dell'applicazione.




L'interfaccia estremamente programmabile di UIApplication e del suo protocollo UIApplicationDelegate permettono di gestire il comportamento della propria applicazione. E' possibile controllare il responso dell'applicazione per cambiare l'orientamento dell'interfaccia attraverso il metodo setStatusBarOrientation:animated::

...
...

[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft];
...
...



Come e' possibile notare dal code snippet sopra, la chiamata al metodo viene effettuata sull'istanza (sharedApplication) disponibile in memoria.

Un'altra importante funzionalita' gestita da UIApplication riguarda l'uso collaborativo di risorse in formato openURL. Diverse applicazioni, infatti, possono cooperare nella gestione delle risorse come email, immagini o numeri di telefono attravero l'uso del metodo openURL:. Ad esempio, un'applicazione che prova a gestire un'istruzione come la seguente:



causera' l'apertura immediata dell'applicazione Mail.

Allo stesso modo, se vogliamo gestire l'apparizione/scomparsa/stile della top bar, abbiamo bisogno di dialogare con un metodo denominato setStatusBarHidden:.

...
...

[[UIApplication sharedApplication] setStatusBarHidden: YES];
...
...



Attenzione al fatto che dopo aver nascosto la barra in alto, si dovra' procedere alla nota chiamata:

...
...

[[UIScreen mainScreen] bounds];
...
...



per ottenere i nuovi estremi dello schermo disponibili. Effettuando la chiamata prima, infatti, non si otterrebbero i nuovi margini derivati dalla scomparsa della topbar (20 pixels).

Se la nostra applicazione utilizza la rete, e' necessario notificare l'utente durante l'uso della connettivita', attraverso un indicatore di network activity posto nella top bar in alto. Per abilitarlo sara' sufficiente invocare un metodo di istanza presente su UIApplication: setNetworkActivityIndicatorVisible:.




Un'ultima caratteristica che e' necessario illustrare e' quella della possibilita' di ottenere da qualunque parte del nostro codice l'istanza alla classe AppDelegate di cui sopra. Questo puo' risultare molto utile, specialmente quando ci troviamo a dover gestire funzionalita' analoghe da tutte le parti della nostra applicazione e strettamente legate al comportamento della nostra applicazione (eventi previsti o imprevisti, dialogo con la "scatola" dell'applicazione). E' possibile ottenere l'istanza della classe attraverso la chiamata al metodo delegate.

...
...

[[UIApplication sharedApplication] delegate];
...
...


Quello che ci viene restituito e' un puntatore all'istanza della classe AppDelegate creata per noi da UIApplicationMain. Ovviamente, tutti i metodi e le properties esposti risulteranno visibili anche dalla sua istanza e quindi utilizzabili in tutte le parti del programma.

Esistono molti altri metodi e caratteristiche della classe UIApplication, che possono essere visionati a questo link della documentazione ufficiale.



AppDelegate e la sua istanza a runtime
La classe che implementa l'application delegate deve adottare il protocollo UIApplicationDelegate. Questo significa che deve/puo' implementare alcuni metodi necessari o meno al suo corretto funzionamento.



Come si puo' notare dal grafico sopra, la sua posizione e' ideale per tutte quelle funzioni/messaggi che riguardano l'intera applicazione e/o suoi comportamenti globali. Il primo metodo che abbiamo gia' avuto modo di vedere approfonditamente nelle scorse lezioni riguarda la notifica di fine caricamento dell'applicazione: applicationDidFinishLaunching:.

Vale la pena segnalare alcuni metodi che potrebbero risultare molto utili durante la stesura della nostra applicazione. application:didFinishLaunchingWithOptions: e' un metodo molto interessante che permette di discernere l'avvio della nostra applicazione a seconda se l'applicazione e' stata aperta da una notifica remota (push notification) oppure per gestire un indirizzo offerto tramite openURL.

Una delle caratteristiche piu' interessanti della nuovo SDK, risulta essere la possibilita' di gestire e personalizzare l'utilizzo della chiamata openURL per lanciare applicazioni di terze parti che riescano a gestire particolari schemi URL. Potremmo, ad esempio, voler creare un nostro protocollo (schema) URL con cui aprire la nostra applicazione:

myApp://parametri/a?=seguire



Come si puo' immaginare, questa caratteristica offre innumerevoli possibilita' per la gestione e visualizzazione di documenti. Per realizzare un'associazione tra uno schema URL e la nostra applicazione dobbiamo innanzitutto editare il file info.plist, presente nel nostro progetto. Aggiungiamo una riga al documento:




e selezioniamo URL types come chiave:




Espandiamo l'elemento appena creato ed aggiungiamo un valore al nuovo campo creato. Puo' essere un valore alfanumerico qualsiasi ma, tipicamente, viene usato un nome in reverse domain order (qualcosa del tipo: org.myapp).




Aggiungiamo un'altro elemento, questa volta figlio del nuovo elemento appena creato.




Selezioniamo il tipo URL Schemes come chiave




Infine, inseriamo la stringa che diverra' il nostro schema primario (ad esempio, "myapp://").




A questo punto, quando l'iphone trovera' un openURL formattato secondo il nostro schema, inviera' un messaggio alla UIApplicationDelegate. Se vogliamo fornire un gestore personalizzato che intervenga all'apertura dell'applicazione, possiamo implementare le nostre personalizzazione all'interno del metodo handleOpenURL:.






Servizi e stato dell'applicazione

Lo stato di un applicazione su iPhone puo' essere molto piu' importante di quanto non lo sia su un'applicazione Desktop. Esistono molti eventi che causano la sospensione o terminazione di un'applicazione sul dispositivo. Questi stati possono presentarsi quando l'utente preme il pulsante Home, blocca lo schermo o riceve una chiamata voce. Per quanto detto, e' importante per un'applicazione conoscere quando il suo stato sta per cambiare, in maniera da salvare tutti i parametri di stato e di configurazione, sospendere le elaborazioni in corso ed effettuare qualunque altra operazione utile alla sospensione dell'applicazione. Visto che non c'e' molto che possa fare un'applicazione per evitare di cambiare il suo stato, puo' almeno prendere tutte le precauzioni perche' una volta terminata l'occorrenza dell'evento che ha causato la sospensione, essa possa ripartire da dove si era bloccata. Questa tecnica si chiama suspend&resume e puo' essere implementata con poche righe di codice su iPhone.

Quando la periferica viene bloccata oppure arriva una telefonata voce, l'applicazione in corso viene sospesa. Quando si verifica tale evento, viene invocato il metodo applicationWillResignActive della nostra classe delegato. Questo metodo puo' essere riscritto e personalizzato in maniera da tenere in considerazione tutte le variabili e parametri che devono andare salvati allo scopo di sospendere correttamente l'applicazione.




Mentre l'applicazione e' sospesa, non gira in background ma viene terminata completamente. Quando viene ripristinata (resume) viene invocato un altro metodo chiamato applicationDidBecomeActive. In questo metodo e' possibile aggiungere tutto il codice necessario al ripristino corretto della nostra applicazione.




Nota che quando l'applicazione viene avviata normalmente (no resume), il metodo applicationDidBecomeActive viene invocato ugualmente. E' compito del programmatore discernere tra i due eventi (lancio semplice e lancio da resume).

Infine, se vogliamo essere notificati circa la chiusura volontaria dell'applicazione, possiamo implementare il metodo applicationWillTerminate:.





Riferimenti
- iPhone Dev Center: UIApplication Class Reference
- iPhone Dev Center: UIScreen Class Reference
- iPhone Dev Center: UIWindow Class Reference
- iPhone Dev Center: UIApplicationDelegate Protocol Reference



    lap1