Ciao, sono Claudia e in questo mio primo articolo su devapp.it vi parlerò di Core Data.
1. Cos’è e cosa non è Core Data
L’esatta definizione della natura di Core Data è di solito l’oggetto di complesse perifrasi, viene di volta in volta frainteso e considerato un ORM o un database.
La documentazione Apple lo definisce come un framework che gestisce la persistenza dei dati e il grafo degli oggetti, che prende concetti dal mondo dei database per portare la gestione dei dati nelle applicazioni a nuovi livelli.
In pratica Core Data è per il Model ciò che Interface Builder è per il View, non si occupa solo della persistenza, ma anche di gestire i dati in memoria, del passaggio da una versione all’altra e dell’undo.
Sia iTunes che apple.com sono ospitati su un server Web Objects, tecnologia da cui si è evoluto il design di Core Data.
2. Considerazioni pratiche
Core Data è in grado di salvare i dati in tre diversi formati: file binario, file xml e database SQLite. Per le app iOS è di gran lunga preferibile SQLite in quanto meno avido di risorse.
Fornisce anche meccanismi per gestire gli inevitabili cambiamenti di formato nella vita dell’app.
E’ possibile aggiungere Core Data ad un’app già esistente, ne parleremo in un prossimo tutorial.
3. In questo tutorial
Costruiremo una piccola applicazione che inserisce e carica dati.
Le classi che utilizzeremo sono:
- NSManagedObjectModel: è la classe che contiene la descrizione dei dati, Entità in Core Data, che vengono salvati nel database. Per questo useremo lo strumento grafico presente in Xcode.
- NSManagedObjectContext: è lo stesso concetto del context di Core Graphics: è come una lavagna. I cambiamenti fatti in un contesto possono essere salvati o annullati. E’ la classe che frequenteremo di più, perché ogni volta che si vogliono ottenere, inserire o cancellare dati chiameremo i suoi metodi.
- NSPersistentStoreCoordinator: dove si setta la path del database utilizzato dall’app, chiamato dal contesto quando registra i cambiamenti.
- NSFetchRequest: la usiamo quando vogliamo fare ricerche nei dati. Funziona in modo simile ai comandi SQL, definiamo quale Entità vogliamo ottenere ed eventuali parametri di filtraggio.
- NSEntityDescription: la usiamo per definire l’Entità per la Fetch Request.
- NSPredicate: la usiamo per definire i criteri di filtraggio per la Fetch Request.
- NSManagedObject: ogni istanza di questa classe rappresenta un’Entità. Accediamo alle sue proprietà utilizzando il Key-Value Coding. Se vogliamo accedere alle proprietà direttamente è necessario subclassare NSManagedObject, ne parleremo in un prossimo tutorial.
4. Cominciamo!
Creiamo un nuovo progetto Window-based e accertiamoci che l’opzione Use Core Data for Storage sia selezionata.
Lo chiameremo CoreDataPart1. Per non perdere tempo con codice estraneo a Core Data inseriremo l’interfaccia direttamente nell’App Delegate (non fatelo a casa!).
Apriamo MainWindow.xib e inseriamo l’interfaccia come nella figura.

5. Sistemiamo gli outlet
Inseriamo nell’App Delegate gli IBOutlet e le IBAction per interagire con l’interfaccia, colleghiamoli in IB e non dimentichiamo di aggiungere UITableViewDataSource alla lista dei protocolli supportati dalla classe e di collegare in IB i delegati della tableView all’AppDelegate.
Apriamo il file CoreDataPart1AppDelegate.h e aggiungiamo queste variabili d’istanza:
IBOutlet UITextField *nameField;
IBOutlet UITextField *surnameField;
IBOutlet UITextField *nickField;
IBOutlet UITableView *contattiTable;
Aggiungiamo i metodi legati ai nostri due pulsanti:
- (IBAction) showContatti;
- (IBAction) addContatto;


6. Impostiamo il comportamento della tastiera – parte prima
Per fare in modo che la tastiera sparisca una volta completato l’inserimento, così da non nascondere la nostra interfaccia, dobbiamo manipolare le impostazioni degli UITextField in IB, settare come delegato l’AppDelegate, inserire UITextFieldDelegate fra la lista dei protocolli supportati nell’AppDelegate ed inserire il metodo textFieldShouldReturn: nel nostro file di implementazione:

In IB settiamo le proprietà della tastiera per ogni UITextField, per primi due sceglieremo Next come Return Key, per l’ultimo Done:


Apriamo il file CoreDataPart1AppDelegate.m e aggiungiamo questo metodo:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
//in questo metodo facciamo in modo che quando l'utente preme 'Next' il focus passi al prossimo campo e quando 'Done' che la tastiera sparisca
if (textField == nameField) {
[surnameField becomeFirstResponder];
}
else if (textField == surnameField) {
[nickField becomeFirstResponder];
}
[textField resignFirstResponder];
return YES;
}
7. Inseriamo i metodi di UITableViewDataSource
Aggiungiamo anche il codice necessario a gestire la tabella: aggiungiamo un NSArray alle variabili d’istanza e i metodi del protocollo UITableViewDataSource:
NSArray *contattiList;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [contattiList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
return cell;
}
8. E’ il momento di Core Data!
Diamo un’occhiata al codice che il template inserisce automaticamente per noi; nell’App Delegate vediamo tre nuove variabili d’istanza, corrispondenti alle classi presentate prima, managedObjectContext, managedObjectModel e persistentStoreCoordinator. Nei loro accessori troviamo cose interessanti, come la path del database utilizzato dall’app in persistentStoreCoordinator, una volta localizzato possiamo aprirlo con qualunque utility SQLite e notarne la struttura insolita.
Inserire dati esterni in un database SQLite creato da Core Data sarà l’oggetto di un prossimo tutorial.
Procediamo nell’esplorazione del progetto, la nostra prossima tappa è il file .xcdatamodel, dove potremo definire graficamente il nostro Model.

9. Definiamo l’Entità
Inseriamo il nostro oggetto nel box Entity, cliccando sul tasto +, modifichiamo il suo nome nel pannello all’estrema destra, chiamiamolo Contatto, la sua classe è NSManagedObject non è una classe astratta e non ha genitore.

10. E le proprietà
Nel box Property aggiungiamo 3 proprietà, utilizzando anche qui il tasto + e scegliendo Add Attribute. Cognome, Nome e Nick, sono tutte di tipo stringa non sono opzionali.

11. E il codice dei metodi che usano Core Data
Ecco definita la nostra classe Model. Adesso andiamo ad aggiungere nel metodo addContatto il codice necessario per inserire l’oggetto nello store di Core Data.
- (IBAction) addContatto {
//Otteniamo il puntatore al NSManagedContext
NSManagedObjectContext *context = [self managedObjectContext];
//Creiamo un'istanza di NSManagedObject per l'Entità che ci interessa
NSManagedObject *contatto = [NSEntityDescription
insertNewObjectForEntityForName:@"Contatto"
inManagedObjectContext:context];
//Usando il Key-Value Coding inseriamo i dati presi dall'interfaccia nell'istanza dell'Entità appena creata
[contatto setValue:surnameField.text forKey:@"cognome"];
[contatto setValue:nameField.text forKey:@"nome"];
[contatto setValue:nickField.text forKey:@"nick"];
//Effettuiamo il salvataggio gestendo eventuali errori
NSError *error;
if (![context save:&error]) {
NSLog(@"Errore durante il salvataggio: %@", [error localizedDescription]);
}
}
Per visualizzare i dati andiamo ad aggiungere questo codice a showContatti:
- (IBAction) showContatti {
//Otteniamo il puntatore al NSManagedContext
NSManagedObjectContext *context = [self managedObjectContext];
//istanziamo la classe NSFetchRequest di cui abbiamo parlato in precedenza
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//istanziamo l'Entità da passare alla Fetch Request
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"Contatto" inManagedObjectContext:context];
//Settiamo la proprietà Entity della Fetch Request
[fetchRequest setEntity:entity];
//Eseguiamo la Fetch Request e salviamo il risultato in un array, per visualizzarlo nella tabella
NSError *error;
NSArray *fo = [context executeFetchRequest:fetchRequest error:&error];
contattiList = [fo retain];
[fetchRequest release];
[contattiTable reloadData];
}
e nel metodo cellForRowAtIndexPath della tableview:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
//istanziamo NSManagedObject perché gli oggetti dentro l'array sono di quel tipo
NSManagedObject *contatto = [contattiList objectAtIndex:indexPath.row];
//accediamo ai dati contenuti dal'oggetto utilizzando il Key-Value Coding
cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", [contatto valueForKey:@"nome"], [contatto valueForKey:@"cognome"]];
cell.detailTextLabel.text = [contatto valueForKey:@"nick"];
return cell;
}
12. Impostiamo il comportamento della tastiera – parte seconda
Al momento la tastiera scompare solo quando schiacciamo Done e non quando premiamo Aggiungi Contatto. Per ovviare a questo inconveniente dobbiamo fare un check in addContatto su quale UITextField abbia il focus e farglielo rilasciare.
Aggiungiamo in cima al metodo addContatto in questo codice:
if ([nameField isFirstResponder]) {
[nameField resignFirstResponder];
} else if ([surnameField isFirstResponder]) {
[surnameField resignFirstResponder];
} else if ([nickField isFirstResponder]){
[nickField resignFirstResponder];
}
Un’altro inconveniente è che siamo liberi di aggiungere allo store di Core Data contatti con campi vuoti. A questo ovvieremo aggiungendo un if che controlli se uno o più dei nostri UITextField è vuoto, e in tal caso mostri un alert e non aggiunga il record.
A questo punto modifichiamo il nostro metodo addContatto in modo che il risultato finale sia questo:
- (IBAction) addContatto {
//apriamo alertView se i campi non sono riempiti
if (nameField.text.length == 0 || surnameField.text.length == 0 || nickField.text.length == 0) {
//Costruiamo l'alert che ci impedisce di inserire un record senza tutti i campi
NSString *titolo = @"Problema";
NSString *messaggio = @"Devi inserire utti i campi!";
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:titolo
message:messaggio
delegate:self
cancelButtonTitle:@"OK, non lo farò più."
otherButtonTitles:nil];
;
;
} else {
//per far sparire la tastiera
if ([nameField isFirstResponder]) {
[nameField resignFirstResponder];
} else if ([surnameField isFirstResponder]) {
[surnameField resignFirstResponder];
} else if ([nickField isFirstResponder]){
[nickField resignFirstResponder];
}
//Otteniamo il puntatore al NSManagedContext
NSManagedObjectContext *context = [self managedObjectContext];
//Creiamo un'istanza di NSManagedObject per l'Entità che ci interessa
NSManagedObject *contatto = [NSEntityDescription
insertNewObjectForEntityForName:@"Contatto"
inManagedObjectContext:context];
//Usando il Key-Value Coding inseriamo i dati presi dall'interfaccia nell'istanza dell'Entità appena creata
[contatto setValue:surnameField.text forKey:@"cognome"];
[contatto setValue:nameField.text forKey:@"nome"];
[contatto setValue:nickField.text forKey:@"nick"];
//Effettuiamo il salvataggio gestendo eventuali errori
NSError *error;
if (![context save:&error]) {
NSLog(@"Errore durante il salvataggio: %@", [error localizedDescription]);
}
//per pulire i campi
NSString *clear = [NSString stringWithFormat:@""];
[nameField setText:clear];
[surnameField setText:clear];
[nickField setText:clear];
}
}
13. Abbiamo finito
Ecco, il nostro programma d’esempio è pronto, testatelo aggiungendo e mostrando contatti.
Il codice di questo esempio è scaricabile dal mio github.
Riassunto
Abbiamo fatto la conoscenza delle classi usate in Core Data, costruito un’entità, inserito e caricato dati.
Nella prossima puntata
- Useremo il wizard di Xcode per generare le classi del Model, in modo da poter accedere alle proprietà direttamente e non tramite il Key Value Coding.
- Aggiungeremo la possibilità di modificare i record
- Parleremo delle differenze fra il paradigma di Core Data e quello dei database relazionali
14 Responses to “T#081 – Guida all’uso di Core Data (Parte 1)”
7 Dicembre 2010
skipottimo articolo! impaziente di leggere la seconda parte! grandi
7 Dicembre 2010
FrancescoOttimo proprio quello che cercavo, aspetto la seconda parte ;).
8 Dicembre 2010
CarloChiaro e completo. Ben fatto!
10 Dicembre 2010
Jack UchihaOttimo tutorial! Una sola domanda: una volta buttato giù lo schema dei dati, come si fa a popolare da codice la tabella?
11 Dicembre 2010
doppioslashQuesto è l’argomento di una delle prossime puntate 🙂
Cmq, l’inserimento dei dati in Core Data resta quello che vediamo in addContatto.
Cambia la fonte: non più l’interfaccia, ma qualcosa tipo un file CVS, o simili, di cui andrà effettuato il parsing.
13 Dicembre 2010
DomiCiao Claudia, grazie x il tutorial, molto interessante 🙂
A quando la 2 parte? ce la regali prima di natale? 😀
21 Dicembre 2010
SoleLunaOttimo tutorial, non riesco però a farlo funzionare ne su un app view-based ne su utility app.
Su window-based, come da tutorial, tutto ok.
Qualche consiglio?
21 Dicembre 2010
doppioslash@Domi sarà fatto 🙂
@SoleLuna nel template view-based non c’è l’opzione per usare Core Data, quindi dovresti inserirlo tu. Tratterò come inserire Core Data in un progetto già esistente in un prossimo tutorial.
In utility-based immagino che il problema sia che vorrai accedere a Core Data dai viewcontroller invece che dall’app delegate, come nel tutorial.
E’ necessario passare il riferimento al NSManagedContext a ogni viewcontroller da cui vuoi accedere a Core Data, alcuni usano l’approccio a singleton, ma sembra che in genere se ne pentano.
Io avevo progettato di trattare la questione insieme a NSFetchedResultsController, più avanti.
30 Dicembre 2010
SoleLuna@doppioslash
Ciao, scusa mi era sfuggita la tua risposta, allora sulla view-based non essendoci il codice core data l’avevo copiato dalla utility-based passando anche il riferimento al NSManagedContext ma proprio su questo passaggio l’app crashava.
Allora son partita da una window based e mi son costruita tutte le view passando ad ognuna di esse NSManagedContex. Tutto funziona correttamente. Ora sto facendo dei test sulla seconda lezione, avrei una curiosità ma forse è meglio che scriva di la 🙂
grazie
24 Marzo 2011
PaoloCiao, se posso permettermi una critica: l’dea di mettere tutto nell’App Delegate è una scelta un po’ infelice dal punto di vista didattica nei confronti di chi sa poco di programmazione Cocoa e sta imparando. Per fortuna in rete ho trovato, dopo molta fatica, il modo per richiamare dal view controller i metodi del core data presenti nell’app delegate, cosa affatto banale e che qui non viene spiegata: http://www.techotopia.com/index.php/An_iPhone_OS_Core_Data_Tutorial
26 Aprile 2011
MicheleCiao doppioslash, volevo sapere come si fa a caricare una view a partire, per esempio, dell’id del contatto?
Immaginiamo di creare un campo univoco “id”
Quindi quando estraiamo i dati abbiamo a disposizione l’id.
Vorrei che a click sulla riga della table, l’applicazione carichi un’altra view che estrae dal database tutte le informazioni del contatto a partire dall’id univoco.
La domanda é: come faccio a passare l’id alla seconda view?
Se vuoi puoi contattarmi a michele.giarratana@gmail.com
Grazie
22 Maggio 2011
Francesco_96Concordo pienamente, inoltre il nuovo articolo promesso non sembra arrivare…
3 Agosto 2012
NicolasCiao io ho gia un progetto avviato e non ho spuntato l’ opzione coredata a suo tempo non e’ che puoi fare a breve un tutorial? Grazie.
22 Agosto 2012
AndreaCiao,
come mai quando modifico gli attributi di una entità e poi provo a lanciare l’applicazione mi ritorna l’errore:
“The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store.”?
Grazie!