Core data intermediate Workshop at NSSpain 2013

66
Diego Freniche / @dfreniche / http://www.freniche.com Core Data Workshop

description

Slides from my talk at NSSpain 2013 on Core Data

Transcript of Core data intermediate Workshop at NSSpain 2013

Page 1: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Core Data Workshop

Page 2: Core data intermediate Workshop at NSSpain 2013

Diego Freniche: programmer & teacher

Page 3: Core data intermediate Workshop at NSSpain 2013

Diego Freniche: programmer & teacher

• @dfreniche

• Freelance Mobile developer: iOS/Android/BB10/webOS/...

• In a former life Java Certifications Collector: SCJP 1.5, SCJP 1.6, SCWCD 1.5, SCBCD 1.3

• Some languages: BASIC, PASCAL, C, C++, Delphi, COBOL, Clipper, Visual Basic, Java, JavaScript, Objective-C

Hello, World!

Page 4: Core data intermediate Workshop at NSSpain 2013

Before we start...

Page 5: Core data intermediate Workshop at NSSpain 2013

Before we start...

• Switch OFF phones

Page 6: Core data intermediate Workshop at NSSpain 2013

Before we start...

• Switch OFF phones

• Been here is funny

Page 7: Core data intermediate Workshop at NSSpain 2013

Before we start...

• Switch OFF phones

• Been here is funny

• ¡Live the moment!¡Carpe diem!

Page 8: Core data intermediate Workshop at NSSpain 2013

Before we start...

• Switch OFF phones

• Been here is funny

• ¡Live the moment!¡Carpe diem!

• Ask me a lot. Don’t yawn

Page 9: Core data intermediate Workshop at NSSpain 2013

What you need (checklist)

• a Mac with OS X capable of running Xcode 4.6.1

• last Xcode 4 installed 4.6.1

• You can also use prerelease software, if you are a registered Apple developer. No support then, sorry :-D

• SimPholders installed: http://simpholders.com

• SQLLite database browser: http://sqlitebrowser.sourceforge.net

• (optional) set $HOME/Library folder visible, using (from a Terminal)

Page 10: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Idea: Creating the Core Data Stack

Page 11: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

The Core Data Stack

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

Page 12: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Doubts

Page 13: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Doubts

• Use Apple’s code?

Page 14: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Doubts

• Use Apple’s code?

• Really?

Page 15: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Doubts

• Use Apple’s code?

• Really?

• Use a singleton?

Page 16: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Doubts

• Use Apple’s code?

• Really?

• Use a singleton?

• Don’t use a singleton?

Page 17: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Doubts

• Use Apple’s code?

• Really?

• Use a singleton?

• Don’t use a singleton?

• Use dependency injection?

Page 18: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Apple’s code

Page 19: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Apple’s code- (void)saveContext{ NSError *error = nil; NSManagedObjectContext *managedObjectContext = self.managedObjectContext; if (managedObjectContext != nil) { if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { // Replace this implementation with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 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:@"Test" 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:@"Test.sqlite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. Typical reasons for an error here include: * The persistent store is not accessible; * The schema for the persistent store is incompatible with current managed object model. Check the error message to determine what the actual problem was. If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory. If you encounter schema incompatibility errors during development, you can reduce their frequency by: * Simply deleting the existing store: [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil] * Performing automatic lightweight migration by passing the following dictionary as the options parameter: @{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES} Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details. */ 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];}

Page 20: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Apple’s code problems

• Core Data Stack inside AppDelegate?

• Really?

• Separation of concerns?

• Only one Managed Object Context

Page 21: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Create our own Core Data Stack

Page 22: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Create our own Core Data Stack

• In one “simple” method

Page 23: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Create our own Core Data Stack

• In one “simple” method

• Singleton / not singleton?

Page 24: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Create our own Core Data Stack

• In one “simple” method

• Singleton / not singleton?

• Use both!

Page 25: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Dependency injection? Or singletons FTW?

• It depends :-)

Page 26: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Idea: using asserts to check threads

Page 27: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Asserts

• Check if we are running UI code in the UI Thread

• Check if we are NOT running Core Data code in the UI Thread

Page 28: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Asserts

• Check if we are running UI code in the UI Thread

• Check if we are NOT running Core Data code in the UI Thread

#define DF_ASSERT_MAIN_THREAD [NSThread isMainThread]?:(NSLog(@"NOT IN MAIN THREAD"),abort())

Page 29: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Idea: create a common UITableView/Core data class

Page 30: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Idea: use an NSManagedObject subclass

Page 31: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Entities Design Tips

Page 32: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Entities Design Tips

• Always add field order

Page 33: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Entities Design Tips

• Always add field order

• Try to create a good UML diagram at first

Page 34: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Entities Design Tips

• Always add field order

• Try to create a good UML diagram at first

• Have an NSString constant with every Entity’s name inside .h

Page 35: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Extend NSManagedObject

• Editor > Create NSManagedObject subclass...

• creates @dynamic properties

• getter / setter generated in runtime (@property in compile time)

• Core Data doesn’t know at compile time if the persistent store is going to be XML or a DB (or in-memory)

Page 36: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Extend NSManagedObject

• overwrite init to call designated initializer

Page 37: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Extend NSManagedObject

• overwrite init to call designated initializer

-(id)init {

NSManagedObjectContext *context = [[CoreDataStack coreDataStack] managedObjectContext];

return [self initWithEntity:[NSEntityDescription entityForName:kRETROITEM_ENTITY inManagedObjectContext:context ] insertIntoManagedObjectContext:context];

}

Page 38: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Validate Properties

• One for every property, if we want it

• Passing parameter by reference

• It should return YES if validation is passed

Page 39: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Validate Properties

• One for every property, if we want it

• Passing parameter by reference

• It should return YES if validation is passed

-(BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError;

Page 40: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Validator for operations

• First thing: must call [super ...]

• Useful to check business rules (using several properties)

Page 41: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Validator for operations

• First thing: must call [super ...]

• Useful to check business rules (using several properties)

- (BOOL)validateForDelete:(NSError **)error

- (BOOL)validateForInsert:(NSError **)error

- (BOOL)validateForUpdate:(NSError **)error

Page 42: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Support for KVO

• Good for Faults

Page 43: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Support for KVO

• Good for Faults

- (void)willAccessValueForKey:(NSString *)key

Page 44: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Idea: use Mogenerator

Page 45: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Mogenerator

created by Jonathan 'Wolf' Rentzsch

Page 46: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Mogenerator (quoting from the web page)

Page 47: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Mogenerator (quoting from the web page)

• http://rentzsch.github.io/mogenerator/

Page 49: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Mogenerator (quoting from the web page)

• http://rentzsch.github.io/mogenerator/

• generates Objective-C code for your Core Data custom classes

• Unlike Xcode, mogenerator manages two classes per entity: one for machines, one for humans

Page 50: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Mogenerator (quoting from the web page)

• http://rentzsch.github.io/mogenerator/

• generates Objective-C code for your Core Data custom classes

• Unlike Xcode, mogenerator manages two classes per entity: one for machines, one for humans

• The machine class can always be overwritten to match the data model, with humans’ work effortlessly preserved

Page 51: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Installing mogenerator

Page 52: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Using it

• it’s a script, so we can launch it from command line

• using iTerm, DTerm, etc.

• Best way: to have it inside our project

• Create a new Aggregate Target (New Target > Other > Aggregate)

• Add Build Phase > Add Run Script

Page 53: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Using it

• it’s a script, so we can launch it from command line

• using iTerm, DTerm, etc.

• Best way: to have it inside our project

• Create a new Aggregate Target (New Target > Other > Aggregate)

• Add Build Phase > Add Run Script

mogenerator --template-var arc=true -m RetroStuffTracker/RetroStuffTracker.xcdatamodeld/RetroStuffTracker.xcdatamodel/

Page 54: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Two classes

• _MyClass.*: machine generated

• *MyClass.*: human edited

• Never, ever recreate the classes again from the Core Data Model

Page 55: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / @dfreniche / http://www.freniche.com

Two classes

• _MyClass.*: machine generated

• *MyClass.*: human edited

• Never, ever recreate the classes again from the Core Data Model

Page 56: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Two classes

Page 57: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

#import "_RetroItem.h"

@interface RetroItem : _RetroItem {}// Custom logic goes here.@end

Two classes

Page 58: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

#import "_RetroItem.h"

@interface RetroItem : _RetroItem {}// Custom logic goes here.@end

#import "RetroItem.h"

@interface RetroItem ()

// Private interface goes here.

@end

@implementation RetroItem

// Custom logic goes here.

@end

Two classes

Page 59: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Idea: use Magical Record

Page 60: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Magical record != avoid Core Data at all costs

Page 61: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Magical record != avoid Core Data at all costs

• Just a bunch of categories to help you write less code

Page 62: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Magical record != avoid Core Data at all costs

• Just a bunch of categories to help you write less code

• You have to know your sh*t

Page 63: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Magical record != avoid Core Data at all costs

• Just a bunch of categories to help you write less code

• You have to know your sh*t

• CocoaPods friendly

Page 64: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Magical record != avoid Core Data at all costs

• Just a bunch of categories to help you write less code

• You have to know your sh*t

• CocoaPods friendly

• Ideal: use Unit testing + Mogenerator + CocoaPods + Magical Record

Page 65: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Magical record != avoid Core Data at all costs

• Just a bunch of categories to help you write less code

• You have to know your sh*t

• CocoaPods friendly

• Ideal: use Unit testing + Mogenerator + CocoaPods + Magical Record

• My point: 7 people, 7 ideas, all great

Page 66: Core data intermediate Workshop at NSSpain 2013

Diego Freniche / http://www.freniche.com

Magical record != avoid Core Data at all costs

• Just a bunch of categories to help you write less code

• You have to know your sh*t

• CocoaPods friendly

• Ideal: use Unit testing + Mogenerator + CocoaPods + Magical Record

• My point: 7 people, 7 ideas, all great

• all different