Address Book è la tecnologia di iOS per la memorizzazione dei contatti degli utenti in un database centralizzato e l’accesso di altre applicazioni a queste informazioni. È, in altre parole, l'infrastruttura che gestisce la rubrica, di un dispositivo iOS che può essere messa a disposizione delle altre applicazione per gestire, ad esempio, la condivisione di risorse o le chiamate telefoniche e così via. La tecnologia comprende:
- Un database.
- I framework AddressBook e AddressBookUI.
- L’applicazione Contacts in iOS.
In questo articolo impareremo a interagire con i contatti dell’utente tramite i due framework elencati in precedenza e per farlo avremo bisogno di
- XCode 4.6 disponibile gratuitamente sul Mac App Store.
- OS X 10.7.4 o successivo.
- Un Mac.
Il PeoplePicker
Per eseguire alcune operazioni comuni sulla rubrica, iOS mette a disposizione dei controller già pronti come il PeoplePicker
(il nome completo è ABPeoplePickerNavigationController
ma per semplicità lo chiameremo semplicemente PeoplePicker) che permette di presentare all’utente la lista dei contatti per sceglierne uno.
È anche possibile creare un’interfaccia personalizzata ma si raccomanda di utilizzare questo controller quando si deve eseguire questo tipo di compito in modo da fornire all’utente un’esperienza coerente con il resto del sistema operativo.
Preparazione del progetto
Creiamo dunque il nostro progetto. Per prima cosa apriamo XCode 4.6 e scegliamo File / New / Project... Nella colonna di sinistra scegliamo Application (dal riquadro iOS) e nell’area di destra scegliamo Single View Application:
Premiamo Next e compiliamo la prossima schermata.
- Product Name: è il nome della nostra app, per esempio MyAddressBookApp.
- Organization Name: nell’AppStore questo campo corrisponde al nome dello sviluppatore o della società in base al tipo di account che si possiede.
- Company Identifier: dovrebbe contenere una stringa che non sia utilizzata da altri sviluppatori a livello mondiale. Se siete titolari di un dominio web digitate il dominio di primo livello seguito da un punto seguito dal dominio di secondo livello (ad esempio www.html.it diventa it.html). Se non possedete un dominio digitate la stringa che preferite intanto non dovrete inviare questa applicazione di test all’AppStore.
- Bundle Identifier: Xcode concatena automaticamente il Company Identifier a un punto e poi al Product Name per creare una stringa che identifica univocamente quest’applicazione a livello mondiale.
- Class Prefix: inseriamo pochi caratteri in maiuscolo che Xcode userà per prefissare i nomi delle classi di questa App: MABA.
Scegliamo iPhone dal campo Devices.
Selezioniamo infine Storyboards e Use Automatic Reference Counting, due componenti che velocizzano enormemente i tempi di sviluppo e permettono di scrivere meno "boilerplate code" (codice ripetitivo).
Premiamo Next. Scegliamo dunque la cartella che conterrà il progetto e premiamo Create (ad esempio la cartella Documenti, Xcode creerà una cartella apposita per il nostro progetto).
Lasciamo qualche secondo a Xcode per generare tutti i file necessari e premiamo CMD + R sulla tastiera per lanciare l’applicazione nel simulatore.
Viene visualizzata una schermata bianca, si tratta del nostro view controller: MABAViewController
.
Applicare il pattern delegate
Tra poco prepareremo un pulsante che, una volta premuto dall’utente, visualizzi un PeoplePicker contenente la lista dei contatti presenti nella rubrica in modo da permette all’utente di selezionarne uno. Quando un contatto viene selezionato il PeoplePicker comunica al suo delegate la scelta effettuata dall’utente: si tratta di un classico esempio di delegation pattern in iOS.
Avremo bisogno di una classe che possa funzionare come delegate del PeoplePicker, che abbia, in altre parole, alcuni metodi specifici: un buon candidato è il nostro MABAViewController
.
Lo useremo nella prossima pagina.
Implementare il protocollo ABPeoplePickerNavigationControllerDelegate
Dobbiamo fare in modo che MABAViewController implementi il protocollo ABPeoplePickerNavigationControllerDelegate
.
Apriamo il file MABAViewController.h e modifichiamolo come mostrato di seguito:
#import <UIKit/UIKit.h> #import <AddressBookUI/AddressBookUI.h> @interface MABAViewController : UIViewController<ABPeoplePickerNavigationControllerDelegate> @end
- Nella schermata principale selezioniamo la voce MyAddressBookApp dal gruppo TARGETS.
- Scendiamo in fondo fino alla sezione Linked Frameworks and Libraries.
- Premiamo il pulsante +.
- Scegliamo AddressBookUI.framework (attenzione a non confonderlo con AddressBook.framework) e premiamo Add.
Il framework è apparso nel Navigator di Xcode sotto la radice del progetto, dobbiamo trascinarlo nel gruppo Frameworks come mostrato di seguito.
Se premiamo CMD + B per compilare Xcode ci mostrerà 4 warning.
Il problema è che, pur avendo dichiarato il nostro view controller come un delegate del PeoplePicker, non abbiamo implementato i metodi obbligatori per ogni classe che implementi ABPeoplePickerNavigationControllerDelegate.
Apriamo MABAViewController.m e aggiungiamo i seguenti metodi prima dell’istruzione @end
.
- (void)peoplePickerNavigationControllerDidCancel: (ABPeoplePickerNavigationController *)peoplePicker { [self dismissViewControllerAnimated:YES completion: nil]; } - (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person { [self dismissViewControllerAnimated: YES completion: nil]; return NO; } - (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson: (ABRecordRef)person property: (ABPropertyID)property identifier: (ABMultiValueIdentifier)identifier { return NO; }
Questi metodi vengono chiamati dal PeoplePicker rispettivamente quando:
- L’utente preme Annulla.
- L’utente seleziona un contatto.
- L’utente seleziona una proprietà di un contatto.
Finalmente, premendo CMD + B, possiamo compilare senza alcun problema.
Il pulsante per visualizzare il PeoplePicker
Creeremo ora un pulsante e collegheremo l’azione del toccare il pulsante alla visualizzazione del PeoplePicker.
- Dal pannello Navigator selezioniamo MainStoryboard.storyboard.
- Accertiamoci che la Object Library sia visibile (View / Utilities / Show Object Library)
- Cerchiamo l’oggetto Round Rect Button e trasciniamolo nella view. Facciamo doppio click su di esso e digitiamo “Seleziona un contatto” seguito da Invio per assegnargli un’etichetta più appropriata.
Colleghiamo la pressione di questo pulsante all’esecuzione di un nuovo metodo:
- Scegliamo View > Assistant Editor > Show Assistant Editor.
- Ora l’area principale dovrebbe essere divisa in 2 parti, a sinistra MainStoryboard.storyboard e a destra MABAViewController.h.
- Facciamo click destro sul pulsante che abbiamo creato poco fa.
- Posizioniamo il puntatore sul cerchietto bianco accanto alla voce Touch Down.
- Teniamo premuto il click principale e trasciniamo il puntatore sul codice di MABAViewController.h, prima dell’istruzione
@end
. - Rilasciamo il click principale.
È apparsa una finestra che ci chiede il nome da assegnare al nuovo metodo: digitiamo showPeoplePicker
e premiamo Connect .
Xcode ha inserito la dichiarazione del metodo showPeoplePicker
. Questo metodo verrà invocato quando l’utente tocca il nostro pulsante. Apriamo il file MABAViewController.m e cerchiamo l’implementazione del metodo (vuota) appena creata da Xcode per noi.
Modifichiamolo come mostrato di seguito.
- (IBAction)showPeoplePicker:(id)sender { ABPeoplePickerNavigationController * peoplePicker = [[ABPeoplePickerNavigationController alloc] init]; peoplePicker.peoplePickerDelegate = self; [self presentViewController: peoplePicker animated: YES completion: nil]; }
- La prima istruzione crea l’oggetto il PeoplePicker.
- La seconda comunica al PeoplePicker che il suo delegate sarà MABAViewController.
- La terza mostra il PeoplePicker all’utente.
A questo punto premiamo CMD + R e verifichiamo quel che accade. Effettivamente ora premendo il pulsante Seleziona un contatto appare la lista dei contatti dell’utente (se la lista è vuota usate il simulatore come fosse un iPhone, premete Home, accedete all’applicazione Contacts e inserite almeno un contatto popolando First Name, Last Name, Birthday e Phone).
Infine possiamo selezionare un contatto o premere Cancel.
Le proprietà Single-Value
Quello che vorremmo fare ora è ricevere dal PeoplePicker l’identificativo del contatto selezionato dall’utente in modo da poter estrarre il suo nome, cognome e data di nascita.
Ancora una volta applichiamo il pattern delegate, infatti come abbiamo visto in precedenza, appena l’utente seleziona un contatto il PeoplePicker invoca il seguente metodo della nostra classe MABAViewController (il secondo dei tre copiati in precedenza, ovvero quello che riceve solo due parametri)
- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson: (ABRecordRef)person { [self dismissViewControllerAnimated:YES completion:nil]; NSString* name = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString* lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty)); NSDate* birthday = (__bridge NSDate*)ABRecordCopyValue(person, kABPersonBirthdayProperty); NSString* titleMessage = [[name stringByAppendingString: @" "] stringByAppendingString: lastName]; NSString* message; if (birthday != nil) { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy"]; NSString* stringFromDate = [formatter stringFromDate:birthday]; message = [@"Nato nel " stringByAppendingString: stringFromDate]; } else { message = @"Data di nascita non disponibile"; } UIAlertView *alert = [[UIAlertView alloc] initWithTitle: titleMessage message: message delegate: nil cancelButtonTitle: @"OK" otherButtonTitles: nil]; [alert show]; return NO; }
La prima riga nasconde il PeoplePicker, mentre la seconda recupera il nome del contatto selezionato invocando una funzione del framework AddressBook che riceve come parametro l’identificativo del contatto e una costante che indica il campo che vogliamo estrarre (il Nome in questo caso).
ABRecordCopyValue(person, kABPersonFirstNameProperty)
Le successive 2 righe del metodo estraggono il cognome e la data di nascita.
La lista dei campi che possiamo estrarre è la seguente:
- kABPersonFirstNameProperty;
- kABPersonLastNameProperty;
- kABPersonMiddleNameProperty;
- kABPersonPrefixProperty;
- kABPersonSuffixProperty;
- kABPersonNicknameProperty;
- kABPersonFirstNamePhoneticProperty;
- kABPersonLastNamePhoneticProperty;
- kABPersonMiddleNamePhoneticProperty;
- kABPersonOrganizationProperty;
- kABPersonJobTitleProperty;
- kABPersonDepartmentProperty;
- kABPersonEmailProperty;
- kABPersonBirthdayProperty;
- kABPersonNoteProperty;
- kABPersonCreationDateProperty;
- kABPersonModificationDateProperty;
La quinta riga concatena il nome e il cognome.
La sesta dichiara un puntatore di tipo NSString e il costrutto if
successivo costruisce un messaggio del tipo Nato nel [ANNO DI NASCITA] nel caso la data di nascita sia presente nella rubrica altrimenti “Data di nascita non disponibile”.
Infine viene inizializzato un oggetto UIAlertView
che mostra le stringhe titleMessage
e message
.
Se proviamo a compilare (CMD + B) otterremo degli errori perché stiamo usando la funzione ABRecordCopyValue
e due costanti kABPersonNameProperty
e kABPersonLastNameProperty
definiti nel framework AddressBook che non abbiamo importato nel progetto, aggiungiamolo:
- Selezioniamo la radice del progetto dal pannello Navigator di Xcode.
- Scegliamo MyAddressBookApp sotto la voce TARGET.
- Scendiamo fino alla sezione Linked Framework and Libraries.
- Aggiungiamo AddressBook.framework.
- Usiamo il pannello Navigator per spostare il framework nel gruppo Frameworks.
Ora apriamo il file MABAViewController.h e aggiungiamo questo import prima dell’istruzione @interface
.
#import <AddressBook/AddressBook.h>
Finalmente possiamo premere CMD + R per verificare nel simulatore che quando scegliamo un contatto appare una popup.
Le proprietà Multi-value
Vediamo ora come gestire le proprietà Multi-Value di un contatto come ad esempio il numero di telefono. Si tratta di tipi proprietà che possono ricorrere più volte all’interno di una contatto e ognuna di esse è definita come una coppia (label, value).
Dichiariamo un nuovo metodo nel file MABAViewController.h come mostrato di seguito (il codice va incollato prima dell’istruzione @end).
- (void)displayPhoneNumbersFromPerson: (ABRecordRef)person;
Questo metodo riceverà un singolo parametro che identifica un contatto della rubrica e mostrerà un messaggio di popup contenente la lista di tutti i numeri di telefono associati a quel contatto.
Apriamo ora MAVAViewController.m e implementiamo il nuovo metodo come descritto di seguito.
- (void)displayPhoneNumbersFromPerson: (ABRecordRef)person { // recupera la lista dei numero di telefono ABMutableMultiValueRef multi = ABRecordCopyValue(person, kABPersonPhoneProperty); CFStringRef label, phoneNumber; // recupera il nome del contatto NSString* name = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString* messageTitle = [@"Numeri di telefono di " stringByAppendingString:name]; NSString* message = @""; NSString * labelAsNSString; NSString * phoneNumberAsNSString; // ottiene la quantità di numeri telefonici int numElm = ABMultiValueGetCount(multi); // per ogni numero telefonico for (CFIndex i = 0; i < numElm; i++) { // estrae l'etichetta del numero (home, iPhone, etc...) label = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(multi, i)); // estrae il numero di telefono phoneNumber = ABMultiValueCopyValueAtIndex(multi, i); // esegue una cast delle 2 CFStringRef precedenti in NSString labelAsNSString = (__bridge NSString*) label; phoneNumberAsNSString = (__bridge NSString*) phoneNumber; // costruisce il messaggio da mostrare message = [message stringByAppendingString:labelAsNSString]; message = [message stringByAppendingString:@": "]; message = [message stringByAppendingString: phoneNumberAsNSString]; if (i < numElm - 1) { message = [message stringByAppendingString:@"n"]; } } // mostra il messaggio UIAlertView* alert = [[UIAlertView alloc] initWithTitle: messageTitle message: message delegate: nil cancelButtonTitle: @"OK" otherButtonTitles: nil]; [alert show]; }
Il metodo definisce una variabile adatta a contenere una lista di valori Multi-Value e la popola estraendo dal contatto (identificato dalla variabile person
) tutti i numeri di telefono tramite la funzioneABRecordCopyValue
del framework AddressBook e la costante kABPersonePhoneProperty
che identifica il numero di telefono.
Poi estrae dalla rubrica il nome del contatto selezionato.
Il costrutto for
scorre tutte le proprietà estratte e usa la funzione AVMultiValueCopyLabelAtIndex
per estrarre la label i-esima e ABAddressBookCopyLocalizedLabel
per convertire la label nella lingua corrente.
Inoltre usa ABMultiValueCopyValueAtIndex
per estrarre il numero di telefono i-esimo.
Dobbiamo ora modificare il metodo
(BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson: (ABRecordRef)person
come mostrato di seguito
- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson: (ABRecordRef)person { [self dismissViewControllerAnimated:YES completion:nil]; [self displayPhoneNumbersFromPerson: person]; return NO; }
La prima riga nasconde il PeoplePicker, la seconda chiama il nostro nuovo metodo e la terza restituisce al PeoplePicker il valore booleano NO
, altrimenti il PeoplePicker ci avrebbe permesso di selezionare una specifica proprietà del contatto.
Premiamo CMD + R e proviamo la nostra App.