Upload
tomas-jukin
View
416
Download
0
Embed Size (px)
DESCRIPTION
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 on 23. 3. 2014 (more at http://www.iconprague.com/detailni-program/#core-data-i-orm-muzete-mit-radi)
Citation preview
Tomáš Jukin@Inza
Core Datathere is an ORM you can like!
Core Data?
Core Data?
-- It’s a big hairy mess!
Core Data?
-- It’s a big hairy mess!YES! but Why?
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?
1) WHY and WHEN to use Core Data?
2) HOW to use Core Data?
WHY?
1) Death
1) Death2) Taxes
1) Death2) Taxes
3) SW Requirements WILL change
“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?
Do I need these features?(= WHEN?)
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!
1) Death2) Taxes
3) SW Requirements WILL change
... 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 implementDo not test
Do not optimize
Core Data =
Do not implementDo not test
Do not optimizeData 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
Fact
Example:
Fast Image? ➜ UIImageView
Fact
CoreData is
Cocoa for Models
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!
Cocoa for Models
Are they powerful?
Cocoa for Models
Are they powerful?YES!
Why NOT to use Core Data?
•Programmer naivity
•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 hardWriting code is easyWhy 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
HOW?
.sqlite
Schema
App
.sqlite
ORM Schema
App
.sqlite
App
Core Data Schema
App
.sqlite Web Store
.sqlite
App
Schema
App
.sqlite Web Store
NSManagedObjectContext
NSManagedObjectContext
NSManagedObjectModel
PersistentObjectStore
PersistentObjectStore
PersistentObjectStore
PersistentStore Coordinator
.sqlite
App
Schema
App
.sqlite Web Store
NSManagedObjectNSManaged
ObjectNSManagedObject
NSManagedObjectContext
NSManagedObjectContext
NSManagedObjectModel
PersistentObjectStore
PersistentObjectStore
PersistentObjectStore
PersistentStore Coordinator
...Complex ORM
=> Complex Setup
Setup
// 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];}
99 rows!
Really ?!
Core Data bites!
The Cure
1) MagicalRecord
github.com/magicalpanda/MagicalRecord
The Cure
1) MagicalRecord
github.com/magicalpanda/MagicalRecord
2) Mogenerator
github.com/rentzsch/mogenerator
The Cure
1) MagicalRecord
github.com/magicalpanda/MagicalRecord
2) Mogenerator
github.com/rentzsch/mogenerator
+ CocoaPods (as usual)
Magical Record
Magical Record
Nice (Rails like) API on Core Data
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
99 rows!
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ [MagicalRecord
setupCoreDataStackWithStoreNamed:@"MyDatabase.sqlite"];
// ... return YES;} - (void)applicationWillTerminate:(UIApplication *)application{ [MagicalRecord cleanUp];}
MR
11 rows!
Fetch
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
[Person findByAttribute:@"age" withValue:@25];
MR
// Query to find all the persons store into the databaseNSArray *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 oldNSArray *personsWhoHave22 = [Person MR_findByAttribute:@"age"
withValue:[NSNumber numberWithInt:25]]; // Query to find the first person store into the databePerson *person = [Person MR_findFirst];
MR
// Query to find all the persons store into the databaseNSArray *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 oldNSArray *personsWhoHave22 = [Person MR_findByAttribute:@"age"
withValue:[NSNumber numberWithInt:25]]; // Query to find the first person store into the databePerson *person = [Person MR_findFirst];
MR
// Query to find all the persons store into the databaseNSArray *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 oldNSArray *personsWhoHave22 = [Person findByAttribute:@"age"
withValue:[NSNumber numberWithInt:25]]; // Query to find the first person store into the databePerson *person = [Person findFirst];
MR
Background Op
[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
Mogenerator
Mogenerator
Because Xcode sucks!
Mogenerator
Because Xcode sucks!
_User for machineUser for human
Mogenerator
Because Xcode sucks!
_User for machineUser for human
+ auto regen!
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íkAutomatic builds for iOS
=> Tomorrow!=> AfterPUB included!
Photo Credits
All photos used are CC from Flickr
http://www.flickr.com/photos/60256070@N05/8330184193/