Core data WIPJam workshop @ MWC'14

85
Diego Freniche / http://www.freniche.com Core Data in 547 easy steps slide 1/373

description

Slides from my talk at WIPJam, MWC'14

Transcript of Core data WIPJam workshop @ MWC'14

Page 1: Core data WIPJam workshop @ MWC'14

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

Core Data in 547 easy steps slide 1/373

Page 2: Core data WIPJam workshop @ MWC'14

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

Core Data Workshop

Page 3: Core data WIPJam workshop @ MWC'14

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 WIPJam workshop @ MWC'14

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

Core Data: developer’s first impression

Page 5: Core data WIPJam workshop @ MWC'14

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

Page 6: Core data WIPJam workshop @ MWC'14

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

Core Data Hello World!

Page 7: Core data WIPJam workshop @ MWC'14

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

That’s easy peasy

Page 8: Core data WIPJam workshop @ MWC'14

Diego Freniche / http://www.freniche.comDiego 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 9: Core data WIPJam workshop @ MWC'14

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

Corollary

• Never, ever use the Core Data checkbox

• BIG design flaws:

• Core Data Stack coupled to AppDelegate:

• can’t reuse in other projects

• separations of concerns, anyone?

• ugly! and just ONE Core Data Stack

• They don’t even use a custom class

Page 10: Core data WIPJam workshop @ MWC'14

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

What is Core Data?

• Poll

• A Framework. Must be important: its name starts with “Core”

• An ORM

• The model of your App

• Technology to help you that has a steep learning curve

Page 11: Core data WIPJam workshop @ MWC'14

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

One of the problems

• Objet - Relational impedance

• SQL: 70s/80s

• OOP: 80s-

Page 12: Core data WIPJam workshop @ MWC'14

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

Exhibit 1: composition

id name idCardPerson

+name: String

+idCard: idCard

+bankAccout: bankAccount

idCard

+number: int+letter: char

+checkLetter()

VARCHAR ¿?

Page 13: Core data WIPJam workshop @ MWC'14

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

Employee

+empNumber: int

Person

+name: String+idCard: idCard

Exhibit 2: inheritance

Hyena

Politician

+moneyTaken: double

Page 14: Core data WIPJam workshop @ MWC'14

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

Exhibit 3: collection modelling

Person

+name: String

+bills: Bill[]

Bill

0..*1

http://en.wikipedia.org/wiki/First_normal_form

Page 15: Core data WIPJam workshop @ MWC'14

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

Relational World

• tables

• cartesian products

• rows/ columns

• normal forms

• Objects

• object collections

• composition

• inheritance

OO World

Page 16: Core data WIPJam workshop @ MWC'14

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

The solution: ORM

• Object Relational Mapper

• Hibernate (Java, .Net)

• Core Data (Cocoa)

• Core Data is our model

• can persist our objects in several ways

Page 17: Core data WIPJam workshop @ MWC'14

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

Which problems will Core Data help me solve?

• Persist my data

• Cache online data

• to speed up things

• to show something to the user when (if) there’s no Internet connection

• Navigate the object graph:

• [([Company allEmployees][0]).boss name];

• Maintain ViewControllers in sync

Page 18: Core data WIPJam workshop @ MWC'14

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

Don’t fight the Frameworks!

• Yes! You need to learn them!

• Everything well thought-out to work with Core Data

• Working with databases without Core Data?

• not so much fun!

• reinvent the wheel

• this is not JavaScript-Land!

Page 19: Core data WIPJam workshop @ MWC'14

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

Core Data terms

• Entity

• an entity in our model == Object in memory == row in table

• Attribute

• Relationship

• Object graph

http://ogre.berniecode.com/ogre_white_paper

Page 20: Core data WIPJam workshop @ MWC'14

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

To get the most out of Core Data

• You need to understand:

• KVC, KVO

• ARC, memory management

• delegate & MVC patterns

• that singletons are evil (more or less)

Page 21: Core data WIPJam workshop @ MWC'14

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

The Core Data Stack

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

Page 22: Core data WIPJam workshop @ MWC'14

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

Managed Object Context

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

Page 23: Core data WIPJam workshop @ MWC'14

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

Our Model

NSManagedObject

NSManagedObject

NSManagedObject

NSManagedObject

Persisted

NSManagedObjectContext

NSObject

In memory only

Page 24: Core data WIPJam workshop @ MWC'14

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

Move away from NSObject!

!@interface  MyClass  :  NSObject  !@end    

!@interface  MyClass  :  NSManagedObject  !@end  

Page 25: Core data WIPJam workshop @ MWC'14

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

Managed Object Context: MOC

• Managed Object Context: in-memory space where CD manages all our model’s objects.

• All CRUD is done against a MOC. We persist data using [context save:]

• Our model’s objects are Managed Objects.

• The MOC needs a Persistent Store Coordinator to save the object graph in persistent store.

Page 26: Core data WIPJam workshop @ MWC'14

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

Managed Object Model

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

Page 27: Core data WIPJam workshop @ MWC'14

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

Managed Object Model

• Maps our model objects into database tables.

• Objects == NSManagedObject

• Classes == NSEntityDescription

• We describe our App entities inside a MOM

• stored inside .xcdatamodeld files in Xcode. Compiles into .momd

• graphic editor / class generator (dumb)

Page 28: Core data WIPJam workshop @ MWC'14

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

Managed Object Model

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

Page 29: Core data WIPJam workshop @ MWC'14

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

Persistent Store Coordinador

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

Makes the mapping between our App’s objects and the physical storage inside the Persistent Object Store.

99% time we’ll work with ONE Object Store, but it’s possible use more than one. For example, a sqlite DB with recipes and another DB with notes, stars, etc. Coordinator: single façade to work with different Stores.!A managed object context can then create an object graph based on the union of all the data stores the coordinator covers

Persistent Object Store

Page 30: Core data WIPJam workshop @ MWC'14

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

Persistent Store Coordinador

Called Coordinator for a reason: serializes operations!Core Data API is NOT Thread safe !Persistent Store Coordinator makes it Thread Safe

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

Managed Object Context

Managed Object Context

Page 31: Core data WIPJam workshop @ MWC'14

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

Persistent Object Store

Managed Object Context

Persistent Store Coordinator

Persistent Object Store

Managed Object Model

NSXMLStoreType (XML only OS X, bad performance)!NSSQLiteStoreType (partial object graph in memory)!NSBinaryStoreType (kind of NIBs, poor performance)!NSInMemoryStoreType (good for testing)

Makes the mapping between our App’s objects and the physical storage inside the Persistent Object Store.

Supported Store Types

Page 32: Core data WIPJam workshop @ MWC'14

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

Creating the Core Data Stack!-(void)setUpCoreDataStack { NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]]; NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; ! NSURL *url = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"Database.sqlite"]; ! NSDictionary *options = @{NSPersistentStoreFileProtectionKey: NSFileProtectionComplete, NSMigratePersistentStoresAutomaticallyOption:@YES}; NSError *error = nil; NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:options error:&error]; if (!store) { NSLog(@"Error adding persistent store. Error %@",error); ! NSError *deleteError = nil; if ([[NSFileManager defaultManager] removeItemAtURL:url error:&deleteError]) { error = nil; store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:options error:&error]; } ! if (!store) { // Also inform the user... NSLog(@"Failed to create persistent store. Error %@. Delete error %@",error,deleteError); abort(); } } ! self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; self.managedObjectContext.persistentStoreCoordinator = psc; } !// http://commandshift.co.uk/blog/2013/09/07/the-core-data-stack/

Page 33: Core data WIPJam workshop @ MWC'14

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

DF Core Data Stack

Warning: really bad code ahead!

Page 34: Core data WIPJam workshop @ MWC'14

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

Dependency injection? Or singletons FTW?

• It depends :-)

Page 35: Core data WIPJam workshop @ MWC'14

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

To create a Core Data Project

• Link against Core Data Framework

• Write some code to create a Core Data Stack

• Test it! In memory (better)

Page 36: Core data WIPJam workshop @ MWC'14

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

SQL in SQLite

• http://sqlite.org/lang.html

• We can have a look at generated SQL:

• -com.apple.CoreData.SQLDebug 1

• Schemes > Edit Scheme > Test > Arguments

Page 37: Core data WIPJam workshop @ MWC'14

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

1

2

3

Page 38: Core data WIPJam workshop @ MWC'14

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

Accessing SQLite

sqlitestudio.pl

Look for your databases in: ! /Users/<usuario>/Library/Application Support/iPhone Simulator/<versión>/Applications/<ID App>/Documents/<archivo SQLite>

Page 39: Core data WIPJam workshop @ MWC'14

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

DDL: Data Definition Language

• CREATE DATABASE, CREATE TABLE, CREATE INDEX, ALTER TABLE, DROP INDEX, ...

• All written by Core Data

• We DO NOT have to create anything: neither tables nor database

• If we make changes, Core Data alters tables, columns, indexes...

Page 40: Core data WIPJam workshop @ MWC'14

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

Modelling

We need a Data Model file to “draw” our model

1st, create all our model’s entities, then add attributes!!Can subclass NSManagedObject to use compiler-time name checking, Xcode’s autofill,...

Page 41: Core data WIPJam workshop @ MWC'14

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

Modelling

• New model versions: Editor > Add Model Version

• Model (.xcdatamodeld) is a folder

• Last version has no number. Oldest with higher number. WTF, Apple! (WTF: What a Tricky Fact)

• 1st time we access model it creates persintent store lazily

Select THIS

Page 42: Core data WIPJam workshop @ MWC'14

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

Versioning and changes

• Activate always lightweight migration

• If we make changes to the model, not changing version, version used in the model and version used to create DB doesn’t match: delete DB.

• Reseting Content and Settings.. in Simulator

• delete (by hand) .sqlite file

Page 43: Core data WIPJam workshop @ MWC'14

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

NSManagedObject

NSManagedObject

NSManagedObjectContext

NSEntityDescription

Page 44: Core data WIPJam workshop @ MWC'14

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

NSManagedObject

• base class implementing all “basic” object model behavior

• We can NOT use Core Data with NSObject, we HAVE TO use NSManagedObject

• Our model classes inherit from NSManagedObject

• not mandatory, but...

• allows us to add logic, use @property, get notifications...

• have to be properly configured

Page 45: Core data WIPJam workshop @ MWC'14

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

Entities Design

• Always add an order field

• Try to create a good UML diagram at first

• Yes, I’ve said UML!

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

Page 46: Core data WIPJam workshop @ MWC'14

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 47: Core data WIPJam workshop @ MWC'14

Diego Freniche / http://www.freniche.comDiego 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 48: Core data WIPJam workshop @ MWC'14

Diego Freniche / http://www.freniche.comDiego 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 49: Core data WIPJam workshop @ MWC'14

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 50: Core data WIPJam workshop @ MWC'14

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 51: Core data WIPJam workshop @ MWC'14

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 52: Core data WIPJam workshop @ MWC'14

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

Support for KVO

• Good for Faults

!

- (void)willAccessValueForKey:(NSString *)key

Page 53: Core data WIPJam workshop @ MWC'14

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

Inserting Entities (INSERT INTO)!

// using NSEntityDescription. Pass: entity name and context

[NSEntityDescription insertNewObjectForEntityForName:@”RetroItems” inManagedObjectContext:context];

[retroItem setValue:@"Spectrum 48K" forKey:@"name"];

[retroItem setValue:@100.00 forKey:@"acquisitionCost"];

[retroItem setValue:[NSDate new] forKey:@"dateAcquired"];

insertNewObjectForEntityForName context save

Context

Page 54: Core data WIPJam workshop @ MWC'14

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

Fetching entities (SELECT)

• Query with NSFetchRequest

• SELECT by definition is unordered

• At least we need to provide

• entity

• order by

Page 55: Core data WIPJam workshop @ MWC'14

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

Step by Step fetch (Select)

!

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

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *entity = [NSEntityDescription entityForName:kRETRO_ITEM_ENTITY inManagedObjectContext:context];

[fetchRequest setEntity:entity];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];

[fetchRequest setSortDescriptors:@[sortDescriptor]];

NSError *error = nil;

NSArray *distincResults = [context executeFetchRequest:fetchRequest error:&error];

1

2

3

4

5

6

Page 56: Core data WIPJam workshop @ MWC'14

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

Order querys

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"fecha_max" ascending:YES];

NSManagedObject returned inside NSArray are unordered unless otherwise we provide NSSortDescription. !1st, we create the sort descriptor

[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];

Then we add to our NSFetchRequest the sort descriptors array:

Page 57: Core data WIPJam workshop @ MWC'14

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

Filtering querysNSManagedObjectContext *moc = [self managedObjectContext];!!NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:moc];!!NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];![request setEntity:entityDescription];!!NSNumber *minimumSalary = ...;!!NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)", minimumSalary];!![request setPredicate:predicate];!!NSError *error = nil;!!NSArray *array = [moc executeFetchRequest:request error:&error];!

NSFetchRequest returns an array of NSManagedObject. We can use a NSPredicate to filter. !Filter/NSPredicate acts here as the SQL WHERE clause. !We need to add that query with setPredicate.

Page 58: Core data WIPJam workshop @ MWC'14

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

• CONTAINS: to query for strings that contain substrings.

• ==: equality operator.

• BEGINSWITH: a pre-made regular expression that looks for matches at the beginning of a string.

• MATCHES: regular expression-like search.

• ENDSWITH: opposite of BEGINSWITH.

• <, >: less than and greater than.

Page 59: Core data WIPJam workshop @ MWC'14

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

Delete Entities (DELETE)

+ (void)deleteInstancesForEntityName:(NSString *)entityName inContext:(NSManagedObjectContext *)context { ! NSFetchRequest *fetch = [[NSFetchRequest alloc] init]; ! [fetch setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:context]]; NSError *error = nil; NSArray *dataObjects = [context executeFetchRequest:fetch error:&error]; for (NSManagedObject *dataObject in dataObjects) { [context deleteObject:dataObject]; } ! NSError *saveError = nil; [context save:&saveError]; }

Page 60: Core data WIPJam workshop @ MWC'14

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

Object identificator

!

NSManagedObjectID *moID = [managedObject objectID];

!

-(NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID

error:(NSError **)error

Page 61: Core data WIPJam workshop @ MWC'14

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

Accessing Properties

Sometimes we don’t want the whole object (NSManagedObject) only the value of a property applying a function (max, min, etc.).

NSFetchRequest *request = [[NSFetchRequest alloc] init];!!NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:context];!![request setEntity:entity];

1. Create NSFetchRequest as usual

[request setResultType:NSDictionaryResultType];

2. Tell NSFetchRequest to return NSDictionary instead NSArray:

NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"creationDate"];!!NSExpression *minExpression = [NSExpression expressionForFunction:@"min:" arguments:[NSArray arrayWithObject:keyPathExpression]];

3. Define the field to calc upon (NSExpression), then the function

Page 62: Core data WIPJam workshop @ MWC'14

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

Accessing Properties

NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];!![expressionDescription setName:@"minDate"];!![expressionDescription setExpression:minExpression];!![expressionDescription setExpressionResultType:NSDateAttributeType];

4. Create a NSExpressionDescription:

[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];!!NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];

5. Set in the query the properties to fetch

if ([objects count] > 0) {! NSLog(@"Minimum date: %@", [[objects objectAtIndex:0] valueForKey:@"minDate"]);!}

6. Access our result:

Page 63: Core data WIPJam workshop @ MWC'14

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

Using Blocks to SortIf Sorting is not trivial maybe NSSortDescription is not enough.

[[NSSortDescriptor alloc] initWithKey:<#(NSString *)#> ascending:<#(BOOL)#> comparator:<#^NSComparisonResult(id obj1, id obj2)cmptr#>

^(id a, id b) { NSNumber *lat1 = [[a valueForKey:@"latitude"] doubleValue]; NSNumber *lon1 = [a valueForKey:@"longitude"] doubleValue]; CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:lat1 longitude:lon1]; NSNumber *lat2 = [[b valueForKey:@"latitude"] doubleValue]; NSNumber *lon2 = [b valueForKey:@"longitude"] doubleValue]; CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:lat2 longitude:lon2]; CLLocationDistance dist_a= [loc1 distanceFromLocation:locUser]; CLLocationDistance dist_b= [loc2 distanceFromLocation:locUser]; if ( dist_a < dist_b ) { return (NSComparisonResult)NSOrderedAscending; } else if ( dist_a > dist_b) { return (NSComparisonResult)NSOrderedDescending; } else { return (NSComparisonResult)NSOrderedSame; } }];

Ejemplo de ordenación de distancias:

Page 64: Core data WIPJam workshop @ MWC'14

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

Relationship example

NSManagedObjectContext *context = [self managedObjectContext];!!NSManagedObject *failedBankInfo = [NSEntityDescription! insertNewObjectForEntityForName:@"FailedBankInfo"! inManagedObjectContext:context];!![failedBankInfo setValue:@"Test Bank" forKey:@"name"];![failedBankInfo setValue:@"Testville" forKey:@"city"];![failedBankInfo setValue:@"Testland" forKey:@"state"];!!NSManagedObject *failedBankDetails = [NSEntityDescription! insertNewObjectForEntityForName:@"FailedBankDetails"! inManagedObjectContext:context];!![failedBankDetails setValue:[NSDate date] forKey:@"closeDate"];![failedBankDetails setValue:[NSDate date] forKey:@"updateDate"];![failedBankDetails setValue:[NSNumber numberWithInt:12345] forKey:@"zip"];![failedBankDetails setValue:failedBankInfo forKey:@"info"];!![failedBankInfo setValue:failedBankDetails forKey:@"details"];!!NSError *error;!!if (![context save:&amp;error]) {! NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);!}!

Writing

Page 65: Core data WIPJam workshop @ MWC'14

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

Relationship example

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];!!NSEntityDescription *entity = [NSEntityDescription! entityForName:@"FailedBankInfo" inManagedObjectContext:context];!![fetchRequest setEntity:entity];!!NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];!!for (NSManagedObject *info in fetchedObjects) {!! NSLog(@"Name: %@", [info valueForKey:@"name"]);!! NSManagedObject *details = [info valueForKey:@"details"];! NSLog(@"Zip: %@", [details valueForKey:@"zip"]);!}!

Reading

Page 66: Core data WIPJam workshop @ MWC'14

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

NSFetchedResultsController

• Controller without interface

• Purpose: “feed” with data an UITableView

• Protocol NSFetchedResultsControllerDelegate

• section “Typical Use”: there’s the code found in template

• connected to a Context: if there are changes of any object inside that context it receives a notification and updates automatically

Page 67: Core data WIPJam workshop @ MWC'14

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

NSFetchedResultsController

Context

NSManagedObject

NSManagedObject

NSManagedObject

SQLite

UITableView NSFetchedResultsController

???

delegate

Page 68: Core data WIPJam workshop @ MWC'14

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

All needed code for NSFetchedResults Cont.

• In the Apple Docs!

Page 69: Core data WIPJam workshop @ MWC'14

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

Idea: use Mogenerator

Page 70: Core data WIPJam workshop @ MWC'14

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

Mogenerator

created by Jonathan 'Wolf' Rentzsch

Page 71: Core data WIPJam workshop @ MWC'14

Diego Freniche / http://www.freniche.comDiego 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 72: Core data WIPJam workshop @ MWC'14

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

Installing mogenerator

Page 73: Core data WIPJam workshop @ MWC'14

Diego Freniche / http://www.freniche.comDiego 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 74: Core data WIPJam workshop @ MWC'14

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 75: Core data WIPJam workshop @ MWC'14

Diego Freniche / http://www.freniche.comDiego 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 76: Core data WIPJam workshop @ MWC'14

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

Page 77: Core data WIPJam workshop @ MWC'14

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

Idea: use Magical Record

Page 78: Core data WIPJam workshop @ MWC'14

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

Magical Record Demo

Warning: really bad code ahead!

Page 79: Core data WIPJam workshop @ MWC'14

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

Page 80: Core data WIPJam workshop @ MWC'14

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

Idea: using asserts to check threads

Page 81: Core data WIPJam workshop @ MWC'14

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

!#ifndef  CDHelloWord_DFThreadAsserts_h  #define  CDHelloWord_DFThreadAsserts_h  !#define  DF_ASSERT_MAIN_THREAD  [NSThread  isMainThread]?:(NSLog(@"NOT  IN  MAIN  THREAD"),abort())  #define  DF_ASSERT_NOT_MAIN_THREAD  ![NSThread  isMainThread]?:(NSLog(@"IN  MAIN  THREAD,  BUT  NOT  EXPECTED"),abort())  !!#endif  

Page 82: Core data WIPJam workshop @ MWC'14

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

Idea: create a common UITableView/Core data class

Page 83: Core data WIPJam workshop @ MWC'14
Page 84: Core data WIPJam workshop @ MWC'14

Questions?

Page 85: Core data WIPJam workshop @ MWC'14

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

Attributions• http://www.flickr.com/photos/paldies/85684217/

• http://www.flickr.com/photos/56380734@N05/6808753611/

• http://www.flickr.com/photos/msimdottv/4339697089/

• http://www.flickr.com/photos/mobilestreetlife/4179063482/

• http://www.flickr.com/photos/owldreams/4428782193/

• http://www.flickr.com/photos/miss_pupik/73160522/

• http://kfannerd713.deviantart.com/art/UNICORN-PUKING-RAINBOWS-WHAAAA-152117816

• http://www.flickr.com/photos/wwworks/4759535950/

• http://www.flickr.com/photos/barto/28135419/sizes/l/in/photolist-3ucFr-7aiwZ-aqWUu-bLebi-cDeVC-jt2YW-jWW1t-kQ25f-m86XP-swafK-yHMbE-yHMcc-yHMda-