Share
Embed
CoreData - there is an ORM you can like!
It doesn't hate you. CoreData is Cocoa for models! But it bites. I show you WHY and WHEN to use CoreData as well as HOW in order to prevent be biten by it.
This slides were presented on my presentation at iCONdev at iCONprague 2014 (more at http://www.iconprague.com/detailni-program/#core-data-i-orm-muzete-mit-radi)
Tomas Jukin
March 23, 2014
Transcript
84 slides
-
Core Data? -- It’s a big hairy mess! YES! but
Why? Real problems are hard. -
Core Data? -- It’s a big hairy mess! YES! but
Why? Real problems are hard. Really? -
Core Data? -- It’s a big hairy mess! YES! but
Why? Real problems are hard. Really? Really. -
Core Data? -- It’s a big hairy mess! YES! but
Why? Real problems are hard. Really? Really. CoreData is hairy -
Core Data? -- It’s a big hairy mess! YES! but
Why? Real problems are hard. Really? Really. CoreData is hairy because it ... -
Core Data? -- It’s a big hairy mess! YES! but
Why? Real problems are hard. Really? Really. CoreData is hairy because it ... ... solves real problems! -
1) WHY and WHEN to use Core Data? 2) HOW
to use Core Data? -
“Do you understand how to structure real-world data access for
Mac and iOS applications better than Apple?” -
“Do you understand how to structure real-world data access for
Mac and iOS applications better than Apple?” -- Nope. -
• Handling future changes to the data schema • Bi-directional
sync with servers • Bi-directional sync with peers (e.g. iPad <-> iPhone) • Undo - Redo • Multithreading long-running operations • Sync data between multiple threads • Notifying faraway code about changes to objects so that they can refresh or update at appropriate intervals Has your own so-called “data stack” features like this? -
But if... I NEVER update my app! My users NEVER
do mistakes! I NEVER use threads! My app is just one view! My data operations ARE all instantaneous! Requirements of my apps NEVER changes! -
... all of that is TRUE for you Core Data
is NOT for you. -
... all of that is TRUE for you Core Data
is NOT for you. Otherwise you SHOULD use it. -
Core Data = Do not implement Do not test Do
not optimize -
Core Data = Do not implement Do not test Do
not optimize Data Stack code used by your app! Apple has already done that! -
Fact Apple’s high-level APIs can be much faster than YOUR
“optimized” code at lower levels -
Cocoa for Models Is UIButton easy? Is UITableView easy? Is
Delegation Pattern easy? Is InterfaceBuilder and outlets easy? -
Cocoa for Models Is UIButton easy? Is UITableView easy? Is
Delegation Pattern easy? Is InterfaceBuilder and outlets easy? NOPE! -
•Programmer naivity ➜ “My app isn’t complex enought” = I
am too lazy to learn it Why NOT to use Core Data? -
•Programmer naivity ➜ “My app isn’t complex enought” = I
am too lazy to learn it ➜ “I dont like large frameworks - I used them in HALF of my app and it was so much work!” = Use entirely it or not. Otherwise it will not save time & effort Why NOT to use Core Data? -
•Programmer naivity ➜ “My app isn’t complex enought” = I
am too lazy to learn it ➜ “I dont like large frameworks - I used them in HALF of my app and it was so much work!” = Use entirely it or not. Otherwise it will not save time & effort •Willful ignorance Why NOT to use Core Data? -
•Programmer naivity ➜ “My app isn’t complex enought” = I
am too lazy to learn it ➜ “I dont like large frameworks - I used them in HALF of my app and it was so much work!” = Use entirely it or not. Otherwise it will not save time & effort •Willful ignorance ➜ “I don’t have any idea what I’m talking about, but it sounds like a bad idea to me” = What? Why NOT to use Core Data? -
So? Reading code is hard Writing code is easy Why
learn when we can re-invent? Really? If this works in your world, Core Data is not for you... -
OK, but when seriously NO? Update HUGE part of DB
in one moment Working with lots of records ➜ RSS Reader app -
Still not ready to use Core Data? Read this: goo.gl/Flzrsk
Numbers here: goo.gl/lNFjdr -
.sqlite App Schema App .sqlite Web Store NSManaged ObjectContext NSManaged
ObjectContext NSManaged ObjectModel Persistent ObjectStore Persistent ObjectStore Persistent ObjectStore Persistent Store Coordinator -
.sqlite App Schema App .sqlite Web Store NSManaged Object NSManaged
Object NSManaged Object NSManaged ObjectContext NSManaged ObjectContext NSManaged ObjectModel Persistent ObjectStore Persistent ObjectStore Persistent ObjectStore Persistent Store Coordinator -
// In the AppDelegate.h @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (void)saveContext; - (NSURL *)applicationDocumentsDirectory; // In the AppDelegate.m - (void)applicationWillTerminate:(UIApplication *)application { // Saves changes in the application's managed object context before the application terminates. [self saveContext]; } - (void)saveContext { NSError *error = nil; NSManagedObjectContext *managedObjectContext = self.managedObjectContext; if (managedObjectContext != nil) { if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } } #pragma mark - Core Data stack /** Returns the managed object context for the application. If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application. */ - (NSManagedObjectContext *)managedObjectContext { if (__managedObjectContext != nil) { return __managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { __managedObjectContext = [[NSManagedObjectContext alloc] init]; [__managedObjectContext setPersistentStoreCoordinator:coordinator]; } return __managedObjectContext; } /** Returns the managed object model for the application. If the model doesn't already exist, it is created from the application's model. */ - (NSManagedObjectModel *)managedObjectModel { if (__managedObjectModel != nil) { return __managedObjectModel; } NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"blaba" withExtension:@"momd"]; __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return __managedObjectModel; } /** Returns the persistent store coordinator for the application. If the coordinator doesn't already exist, it is created and the application's store added to it. */ - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (__persistentStoreCoordinator != nil) { return __persistentStoreCoordinator; } NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"blaba.sqlite"]; NSError *error = nil; __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return __persistentStoreCoordinator; } #pragma mark - Application's Documents directory /** Returns the URL to the application's Documents directory. */ - (NSURL *)applicationDocumentsDirectory { return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; } -
Magical Record Nice (Rails like) API on Core Data You
still can dive in! -
// In the AppDelegate.h @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (void)saveContext; - (NSURL *)applicationDocumentsDirectory; // In the AppDelegate.m - (void)applicationWillTerminate:(UIApplication *)application { // Saves changes in the application's managed object context before the application terminates. [self saveContext]; } - (void)saveContext { NSError *error = nil; NSManagedObjectContext *managedObjectContext = self.managedObjectContext; if (managedObjectContext != nil) { if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } } #pragma mark - Core Data stack /** Returns the managed object context for the application. If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application. */ - (NSManagedObjectContext *)managedObjectContext { if (__managedObjectContext != nil) { return __managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { __managedObjectContext = [[NSManagedObjectContext alloc] init]; [__managedObjectContext setPersistentStoreCoordinator:coordinator]; } return __managedObjectContext; } /** Returns the managed object model for the application. If the model doesn't already exist, it is created from the application's model. */ - (NSManagedObjectModel *)managedObjectModel { if (__managedObjectModel != nil) { return __managedObjectModel; } NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"blaba" withExtension:@"momd"]; __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return __managedObjectModel; } /** Returns the persistent store coordinator for the application. If the coordinator doesn't already exist, it is created and the application's store added to it. */ - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (__persistentStoreCoordinator != nil) { return __persistentStoreCoordinator; } NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"blaba.sqlite"]; NSError *error = nil; __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return __persistentStoreCoordinator; } #pragma mark - Application's Documents directory /** Returns the URL to the application's Documents directory. */ - (NSURL *)applicationDocumentsDirectory { return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; } CD -
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions { [MagicalRecord setupCoreDataStackWithStoreNamed: @"MyDatabase.sqlite"];
// ... return YES; } - (void)applicationWillTerminate:(UIApplication *) application { [MagicalRecord cleanUp]; } MR -
NSArray *fetchedObjects; NSManagedObjectContext *context = [self managedObjectContext]; NSFetchRequest *fetch
= [[NSFetchRequest alloc] init]; NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context]; [fetch setEntity:entityDescription]; [fetch setPredicate:[NSPredicate predicateWithFormat:@"age = %d", 25]]; NSError *error = nil; fetchedObjects = [context executeFetchRequest:fetch error:&error]; if([fetchedObjects count] == 1) return [fetchedObjects objectAtIndex:0]; else return nil; CD -
NSArray *fetchedObjects; NSManagedObjectContext *context = [self managedObjectContext]; NSFetchRequest *fetch
= [[NSFetchRequest alloc] init]; NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context]; [fetch setEntity:entityDescription]; [fetch setPredicate:[NSPredicate predicateWithFormat:@"age = %d", 25]]; NSError *error = nil; fetchedObjects = [context executeFetchRequest:fetch error:&error]; if([fetchedObjects count] == 1) return [fetchedObjects objectAtIndex:0]; else return nil; CD -
// Query to find all the persons store into the
database NSArray *persons = [Person MR_findAll]; // Query to find all the persons store into the database order by their 'firstname' NSArray *personsSorted = [Person MR_findAllSortedBy:@"firstname" ascending:YES]; // Query to find all the persons store into the database which have 25 years old NSArray *personsWhoHave22 = [Person MR_findByAttribute:@"age" withValue:[NSNumber numberWithInt:25]]; // Query to find the first person store into the databe Person *person = [Person MR_findFirst]; MR -
// Query to find all the persons store into the
database NSArray *persons = [Person MR_findAll]; // Query to find all the persons store into the database order by their 'firstname' NSArray *personsSorted = [Person MR_findAllSortedBy:@"firstname" ascending:YES]; // Query to find all the persons store into the database which have 25 years old NSArray *personsWhoHave22 = [Person MR_findByAttribute:@"age" withValue:[NSNumber numberWithInt:25]]; // Query to find the first person store into the databe Person *person = [Person MR_findFirst]; MR -
// Query to find all the persons store into the
database NSArray *persons = [Person findAll]; // Query to find all the persons store into the database order by their 'firstname' NSArray *personsSorted = [Person findAllSortedBy:@"firstname" ascending:YES]; // Query to find all the persons store into the database which have 25 years old NSArray *personsWhoHave22 = [Person findByAttribute:@"age" withValue:[NSNumber numberWithInt:25]]; // Query to find the first person store into the databe Person *person = [Person findFirst]; MR -
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) { Post *post
= [Post createInContext:localContext]; // photo processing // update post from photo processing } completion:^(BOOL success, NSError *error) { // This is called when data done, // and is called on the main thread }]; MR -
But the docs sucks too! It coves the basics, but
I DO NEED to DIVE in! goo.gl/9fNe0z goo.gl/durEGR -
Recap •You want to use Core Data •You want to
use NSFetchResultsController •You want to use MagicalRecord •You want to use Mogenerator •YOP, setup of that is epic! •You want to use CocoaPods •YOP, docs for CoreData and MagicalRecord sucks LET the CoreData work for you! -
For-Mobile http://srazy.info/for-mobile Pondělí, 24. 3. 2014 19:00 Jakub Hladík Automatic
builds for iOS => Tomorrow! => AfterPUB included! -
Photo Credits All photos used are CC from Flickr http://www.flickr.com/photos/
60256070@N05/8330184193/