Tutte le applicazioni hanno bisogno di salvare delle informazioni in modo persistente all’interno del device, che siano soltanto delle opzioni di configurazione, piuttosto che una struttura dati più complessa.
Su iOS si hanno a disposizione diverse teniche per raggiungere lo scopo, che vanno dal salvare un file di testo, serializzare delle classi base in file xml (plist), utilizzare il protocollo NSCoding e la classe NSKeyedArchiver per serializzare gli oggetti in un file binario oppure per strutture più complesse si può utilizzare un database come SQLite, ultralite o un ORM come Coredata.
Oggi al ventaglio di opzioni si aggiunge un nuovo framework: Realm (homepage), che si presenta come “…a mobile database: a replacement for SQLite & Core Data“. Le premesse sono importanti, vediamo quindi il framework all’opera.
Per installare il framework la via più facile è tramite cocoapods, basta infatti aggiungere:
pod 'Realm'
al Podfile per poter iniziare ad usarlo.
Il framework richiede che gli oggetti da memorizzare all’interno del database siano subclass di RLMObject un po’ come con CoreData e i suoi NSManagedObject, ma le similitudini si fermano qui, perché con Realm è possibile scrivere direttamente le nostre classi senza passare dal file xcdatamodel.
Quindi supponiamo che si voglia salvare oggetti di tipo ‘Cutomer’ la classe potrebbe essere così:
#import "RLMObject.h" @interface Customer : RLMObject @property (nonatomic) NSString *firstName; @property (nonatomic) NSString *lastName; @property (nonatomic) NSString *address; @property (nonatomic) NSString *city; @property (nonatomic) NSString *zipCode; - (NSString *)fullAddress; @end
e l’aspetto forse più comodo è che gli oggetti possono essere creati direttamente, senza passare da NSContextManager, quindi quando vorrò creare un nuovo Customer dovrò semplicemente scrivere:
Customer *newCustomer = [[Customer alloc] init]; [newCustomer setFirstName:@"Mario"]; [newCustomer setLastName:@"Rossi"]; [newCustomer setAddress:@"Viale Zara 1"]; [newCustomer setCity:@"Milano"]; [newCustomer setZipCode:@"20144"];
Ma la parte più importante è come si salvano gli oggetti? A differenza di coredata/sqlite non è necessaria alcuna configurazione, esiste infatti una classe singleton già configurata per gli utilizzi più comuni ed è già pronta da utilizzare. Possiamo quindi accedere alla nostra istanza del “database” in questo modo:
RLMRealm *realm = [RLMRealm defaultRealm];
Per Realm tutte le operazioni di scrittura devono essere gestite all’interno di una transaction (chi è pratico di database sa di cosa sto parlando):
[realm beginWriteTransaction]; // qui inseriamo il codice per salvare l'oggetto [realm commitWriteTransaction];
Il codice per salvare il nostro customer nel database non poteva essere più semplice infatti basta digitare:
[realm addObject:newCustomer];
per salvare in modo permanente il customer all’interno del database.
Riepilogando il nostro codice per creare e salvare il customer si presenta così
Customer *newCustomer = [[Customer alloc] init]; [newCustomer setFirstName:@"Mario"]; [newCustomer setLastName:@"Rossi"]; [newCustomer setAddress:@"Viale Zara 1"]; [newCustomer setCity:@"Milano"]; [newCustomer setZipCode:@"20144"]; RLMRealm *realm = [RLMRealm defaultRealm]; [realm beginWriteTransaction]; [realm addObject:newCustomer]; // [realm commitWriteTransaction];
Potrebbe essere più facile di così?
Operazioni CRUD
Abbiamo visto come creare un nuovo customer e come salvarlo nel database, in termini di operazioni CRUD (http://en.wikipedia.org/wiki/Create,_read,_update_and_delete) abbiamo visto la ‘C’ di create. Ma da un framework il cui obiettivo è sostituire coredata ci aspettiamo che anche le altre operazioni siano gestite in modo altrettanto semplice.
‘R’ come Read:
Si possono effettuare delle select sul database partendo direttamente dal modello (ActiveRecord design pattern) infatti tutte le subclass di RLMObject ereditano metodi come: objectsWhere:, objectsWithPredicate:
‘U’ come Update:
Per aggiornare un record bisogna editare direttamente l’oggetto associato, newCustomer.firstName = @”Luigi”; e il nuovo valore sarà automaticamente salvato nel database
‘D’ come Delete:
La cancellazione si fa a partire dal realm utilizzando i metodi: deleteObjects: e deleteObject:
Come approfondire
Se l’articolo vi ha incuriosito e volete approfondire lo studio di questo framework, date un’occhiata alla documentazione (ben fatta) presente sul sito http://realm.io/docs/cocoa/0.86.2/ dove è spiegato come gestire le relazioni o come ricevere delle notifiche ogni volta che un record viene aggiornato (utile per aggiornare la UI), come utilizzare Realm per salvare dati provenienti da un webserice rest and so on.
Discaimer
Realm è un’azienda alla quale non sono e non siamo legati in alcun modo. Al momento il prodotto è free e opensource, ma non mi meraviglierebbe una versione a pagamento in un prossimo futuro se il prodotto dovesse avere particolarmente successo. Per info visitare la pagina http://realm.io/pricing/
Buona programmazione a tutti!










6 Responses to “Come gestire la persistenza nelle applicazioni iOS con Realm”
13 Ottobre 2014
EmanueleCiao! Sono Emanuele da Realm.
Grazie per l’articolo! Non abbiamo alcun piano di rilasciare un’altra versione di Realm per iOS o Android a pagamento e ci siamo impegnati a sviluppare questi prodotti gratuitamente e come open-source per il futuro a noi visibile. Abbiamo giá introiti provenienti dalla vendita di prodotti aggiuntivi per il mondo Enterprise e servizi costruiti attorno a Realm, in modo da poter mantenere Realm per iOS e Android gratuiti 🙂
13 Ottobre 2014
IgnaziocCiao Emanuele!
sono contento che tu abbia trovato questo articolo.
Apprezzo molto il vostro lavoro e non avrei potuto pensare ad un modo più semplice di implementare la persistenza su iOS.
L’unica cosa che mi suona un po’ ‘anni 90’ è il begin transacion – end transaction che sono sicuro verrà rimpiazzata a breve (se non lo è già) da un bel block, così come del resto hanno fatto con coredata.
Ciao e grazie per il commento.
14 Ottobre 2014
EmanueleCiao Ignazio,
le transazioni sono in realtá uno dei punti forti di Realm. Senza di esse non saremmo in grado di dare garanzie ACID e la vita degli sviluppatori si complicherebbe a dismisura. Esse permettono di avere un modo piuttosto elegante e funzionale di gestire dati in thread diversi senza doversi preoccupare di problemi di concurrency. Tutto questo con i block non é al momento possibile.
Se hai domande o richieste non esitare a contattarmi e se l’inglese non é un problema ti posso mettere in contatto con i nostri sviluppatori iOS (io mi occupo di Android, ma visto che sono italiano non potevo esimermi dal commentare il tuo post) 🙂
15 Ottobre 2014
IgnaziocHo chiaro l’utilizzo delle transaction, quello che volevo sottolineare è proprio la sintassi, cioè il dover scrivere il codice tra le due invocazioni begin – end transaction.
L’utilizzo dei block è assolutamente possibile e solleverebbe l’utente dal dover invocare esplicitamente questi metodi.
In pratica l’utente avrebbe qualcosa tipo:
[realm writeWithTransaction:^{ //all the operations with realm }];e nella libreria dovresti avere qualcosa tipo
- (void)writeWithTransaction:(void (^)(void))block { [self beginWriteTransaction]; block(); [self endWriteTransaction]; }In realtà è una roba molto semplice che può essere anche aggiunta in una category della classe RLMRealm.
No, l’inglese non è un problema, ma al momento non sto usando attivamente la libreria quindi non ho particolari richieste.
19 Ottobre 2014
TiziBeatCiao. Bell’articolo.
Ti pongo una domanda, visto che non mi è ben chiara una cosa.
Non ho capito come si fa ad editare un record.
Immaginiamo ad esempio che io abbia una classe, e una property “id” che funge da chiave. Se voglio editare ad esempio quel determinato record, come faccio?
E nel caso la chiave non fosse presente?
20 Ottobre 2014
IgnazioCPer aggiornare un record che hai già salvato ti serve prima trovare quel record, e lo puoi fare tramite i metodi esposti dalla classe RLMObject (e sue sottoclassi), una volta che hai trovato l’oggetto modifichi quello che ti serve all’interno di una write transaction ed hai finito.
Qui trovi le indicazioni su come cercare un oggetto: http://realm.io/docs/cocoa/0.86.3/#queries
In pratica se ti serve fare un check con l’id ti basta
[TuaClass objectsWhere:@"id == 'XXXXXX'"];