U S I N G R E A L M F O R D ATA P E R S I S T E N C E :
A C O M PA R I S O N W I T H C O R E D ATA
J O S H U A D U T T O N : M O B I L E D E V E L O P E R A N D C . T. O . A T H I T L A B S
Cactus Charro by Daniel Garrido
A P R I L 1 6 , 2 0 1 5
Slide 2
Slide 2 text
O R
Cactus Charro by Daniel Garrido
Slide 3
Slide 3 text
W H Y I S TA R T E D U S I N G R E A L M ,
E N D E D U P U S I N G C O R E D ATA ,
B U T WA N T T O M O V E B A C K T O R E A L M
J O S H U A D U T T O N : M O B I L E D E V E L O P E R A N D C . T. O . A T H I T L A B S
Cactus Charro by Daniel Garrido
A P R I L 1 6 , 2 0 1 5
Slide 4
Slide 4 text
W H AT I S C O R E D ATA ?
• Object graph
• XML, Binary, or SQLite data stores
• Change management, serializing to disk, and queries
• Extremely flexible, but somewhat confusing API
X-Ray Bukethead by Daniel Garrido
Slide 5
Slide 5 text
A N O T E O N L E A R N I N G C O R E D ATA
• DON’T:
• Use a 3rd party library to try to simplify your code
(like Magical Record)
• Use a singleton (global state can cause problems)
• Rely only on Apple’s Core Data Programming Guide
X-Ray Bukethead by Daniel Garrido
Slide 6
Slide 6 text
A N O T E O N L E A R N I N G C O R E D ATA
• DO:
• Read these tuts+ tutorials (Core Data From Scratch):
http://code.tutsplus.com/series/core-data-from-scratch--cms-653
• Write your own categories/helpers for boiler plate code. You can use
Robert Brown’s code to learn from:
https://github.com/rob-brown/RBCoreDataStack
• Explicitly state which Managed Object Context you are using (for
example, don’t have helper methods that assume you are using your
main context)
• Read this blog post about a good core data setup:
http://martiancraft.com/blog/2015/03/core-data-stack/
X-Ray Bukethead by Daniel Garrido
Slide 7
Slide 7 text
X-Ray Bukethead by Daniel Garrido
W H AT I S R E A L M ?
• A NO-SQL database written specifically for mobile
• Cross platform (written in C++ with Cocoa and Java
interfaces)
• Simple API
Slide 8
Slide 8 text
W R I T I N G : C O R E D ATA
• All objects (NSManagedObjects) belong to a
Managed Object Context.
• The Managed Object Context keeps track of changes.
Some people call Managed Object Contexts “scratch
pads”.
• All changes are persisted to disk when the Managed
Object Context is saved to the Persistent Store.
Flying Taco by Daniel Garrido
Slide 9
Slide 9 text
W R I T I N G : C O R E D ATA
// Skipping over lots of boiler plate code to setup the main context
// and the persistent store coordinator
Person *person =
[NSEntityDescription insertNewObjectForEntityForName:@"Person"
inManagedObjectContext:self.mainContext];
person.name = @"Joshua";
// Sometime later save the Managed Object Context to the Persistent Store
// Many do this when the app quits or goes to the background
NSError *error;
BOOL success = [self.mainContext save:&error];
Flying Taco by Daniel Garrido
Slide 10
Slide 10 text
W R I T I N G : R E A L M
Flying Taco by Daniel Garrido
• All objects belong to a Realm (think of a Realm as it’s own
database file).
• All changes to an object (addition, modification and
deletion) have to be done within a write transaction.
• Write transactions are atomic and thread safe. This also
means that they can block the thread, particularly if several
write transactions are queued.
• Changes are persisted immediately and available on all
threads.
Slide 11
Slide 11 text
Flying Taco by Daniel Garrido
W R I T I N G : R E A L M
// Adding a new object
Person *person = [Person new];
person.name = @"Joshua";
// Get the default Realm
RLMRealm *realm = [RLMRealm defaultRealm];
// You only need to do this once (per thread)
[realm beginWriteTransaction];
[realm addObject:person];
[realm commitWriteTransaction];
// Or
[realm transactionWithBlock:^{
[realm addObject:person];
}];
Slide 12
Slide 12 text
Flying Taco by Daniel Garrido
W R I T I N G : R E A L M
// Update an object with a transaction
[realm beginWriteTransaction];
person.name = @"Joshua";
[realm commitWriteTransaction];
// Delete an object with a transaction
[realm beginWriteTransaction];
[realm deleteObject:person];
[realm commitWriteTransaction];
Slide 13
Slide 13 text
F E T C H I N G : C O R E D ATA
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:@“Person"];
fetchRequest.predicate =
[NSPredicate predicateWithFormat:@"name BEGINSWITH[c] %@", prefix];
fetchRequest.sortDescriptors =
@[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]];
NSError *error;
NSArray *results =
[self.mainContext executeFetchRequest:fetchRequest error:&error];
Nestereo by Daniel Garrido
Slide 14
Slide 14 text
Nestereo by Daniel Garrido
F E T C H I N G : C O R E D ATA
• Two ways to conserve memory:
1. Array with NSManagedObjects that are faults
(persistent variables are not yet initialized)
2. NSFetchResultsController: optimized for displaying
fetch results in a table view or collection view.
Slide 15
Slide 15 text
Nestereo by Daniel Garrido
F E T C H I N G : R E A L M
RLMResults *results =
[[Person objectsWhere:@"name BEGINSWITH[c] %@", prefix]
sortedResultsUsingProperty:@"name" ascending:YES];
Slide 16
Slide 16 text
Nestereo by Daniel Garrido
RLMResults
• Kind of like a NSFetchRequest with a simple array
interface to access the results
• Lazy loads
• Able to chain queries
• Gotcha: results aren’t cached and objects can change
(though in the future, results will be frozen during fast
enumeration:
https://github.com/realm/realm-cocoa/issues/1179)
Slide 17
Slide 17 text
Nestereo by Daniel Garrido
RLMResults: counts
NSUInteger count = [[Person allObjects] count];
Slide 18
Slide 18 text
Nestereo by Daniel Garrido
RLMResults: iteration
for (Person *person in [Person allObjects]) {
// do something with the person
}
Hippy Happy Duck by Daniel Garrido
C O N C U R R E N C Y: C O R E D ATA
• You can’t pass a Managed Object Context across multiple
threads
• You can’t pass a Managed Object across multiple threads (you
need to query for it or access it by objectId)
• Since Managed Object Contexts don’t usually persist to the
store immediately, you need a way to sync changes from a
context on one thread to a context on another:
• Change notifications
• Parent/child contexts
Slide 21
Slide 21 text
Hippy Happy Duck by Daniel Garrido
C O N C U R R E N C Y: R E A L M
• You can use the same Realm on multiple threads, but
you need to access it separately on each thread
• You can’t pass a Realm Object across multiple threads
(you need to query for it or access it by primary key)
• Since write transactions are atomic, you don’t need to
sync changes
Slide 22
Slide 22 text
Hippy Happy Duck by Daniel Garrido
C O N C U R R E N C Y: R E A L M
dispatch_async(backgroundQueue, ^{
// Get realm for this thread
RLMRealm *realm = [RLMRealm defaultRealm];
Person *person = [Person objectForPrimaryKey:key];
[realm beginWriteTransaction];
person.name = @"William";
// Commit the write transaction
// to make this data available to other threads
[realm commitWriteTransaction];
});
Slide 23
Slide 23 text
Hippy Happy Duck by Daniel Garrido
C O N C U R R E N C Y: R E A L M
// Observe Realm notifications for changes on other threads
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
[myViewController updateUI];
}];
// Notifications are currently very limited
// You are notified that a realm has changed, but not what the changes are
// Fine-grained notifications are "coming soon"
Slide 24
Slide 24 text
R E A L M : O T H E R F E AT U R E S
• Encryption
• In memory realms
• Cross-platform Realm files
• Migrations
• Realm Browser
• Sharing Realms between processes (Apple Watch and
Extensions)
Sombrero Bip-Bop by Daniel Garrido
Slide 25
Slide 25 text
Sombrero Bip-Bop by Daniel Garrido
R E A L M : A D VA N TA G E S
• Fast
• Simple API
• Follows the fail fast principle
• Easy to manage concurrency
• Active and helpful development team (Github, Stack
Overflow, Twitter, Google Group)
• Cross platform
Slide 26
Slide 26 text
Sombrero Bip-Bop by Daniel Garrido
R E A L M : C U R R E N T L I M I TAT I O N S
• Still in beta
• No fine-tuned notifications (duplicating NSFetchedResultsController is hard)
- fix coming soon
• Does not support KVO
- fix coming soon
• NSDate is truncated to second (use NSTimeInterval instead)
- fix coming soon
• Does not support nil properties (NSString, NSData, NSNumber, etc.)
- fix coming soon
• Doesn’t support as many types of queries as Core Data or SQLite
- they plan on supporting more in the future