Importing into Core Data
Episode
#12
|
22 minutes
| published on
April 19, 2012
Free Video
In this screencast I'll pull down data from an API, map the JSON to a Core Data Managed Object and import them in bulk on a background thread. Then we'll display the imported content in a UITableView using NSFetchedResultsController.
Want more? Subscribers can view all 470 episodes. New episodes are released regularly.
Episode Links
This project requires mogenerator to be installed. You can install it with homebrew:
brew install mogenerator
Having mogenerator invoked automatically after each build
Add a new Run-Script Build Phase with the following contents:
MODELS_DIR="BeerBrowser/DataModel"
DATA_MODEL_PACKAGE="$MODELS_DIR/Beers.xcdatamodeld"
CURRENT_VERSION=`/usr/libexec/PlistBuddy "$DATA_MODEL_PACKAGE/.xccurrentversion" -c 'print _XCCurrentVersionName'`
mogenerator --template-var arc=true --model "$DATA_MODEL_PACKAGE/$CURRENT_VERSION" --output-dir "$MODELS_DIR/"
Core Data-friendly nil support on NSDictionary
#import "NSDictionary+ObjectForKeyOrNil.h"
@implementation NSDictionary (ObjectForKeyOrNil)
- (id)objectForKeyOrNil:(id)key {
id val = [self objectForKey:key];
if ([val isEqual:[NSNull null]]) {
return nil;
}
return val;
}
@end
Mapping from JSON to Core Data
This is a manual approach to mapping from JSON objects. It's also possible to devise a more "clever" convention-based scheme.
- (void)updateAttributes:(NSDictionary *)attributes {
self.name = [attributes objectForKeyOrNil:@"name"];
self.address1 = [attributes objectForKeyOrNil:@"address1"];
self.address2 = [attributes objectForKeyOrNil:@"address2"];
self.city = [attributes objectForKeyOrNil:@"city"];
self.state = [attributes objectForKeyOrNil:@"state"];
self.country = [attributes objectForKeyOrNil:@"country"];
self.postalCode = [attributes objectForKeyOrNil:@"postalCode"];
self.details = [attributes objectForKeyOrNil:@"description"];
self.website = [attributes objectForKeyOrNil:@"website"];
}
Fetching an entity by a property
+ (Brewery *)breweryWithServerId:(NSInteger)serverId
usingManagedObjectContext:(NSManagedObjectContext *)moc {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[Brewery entityName]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"serverId = %d", serverId]];
[fetchRequest setFetchLimit:1];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(@"ERROR: %@ %@", [error localizedDescription], [error userInfo]);
exit(1);
}
if ([results count] == 0) {
return nil;
}
return [results objectAtIndex:0];
}
Fetched results controller initialization
- (void)loadBreweries {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[Brewery entityName]];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:@"name", @"city", @"state", @"country", nil]];
[fetchRequest setFetchBatchSize:40];
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortByName]];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[[BeersDataModel sharedDataModel] mainContext]
sectionNameKeyPath:nil
cacheName:nil];
[_fetchedResultsController performFetch:nil];
}
Listening for changes to a background Managed Object Context
// in viewDidLoad...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
- (void)contextDidSave:(NSNotification *)notification {
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObjectContext *mainContext = [[BeersDataModel sharedDataModel] mainContext];
[mainContext mergeChangesFromContextDidSaveNotification:notification];
[self loadBreweries];
[self.tableView reloadData];
});
}
Implementing the UITableViewDataSource methods with NSFetchedResultsController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[_fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id<NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Brewery *brewery = [_fetchedResultsController objectAtIndexPath:indexPath];
/* Standard UITableViewCell stuff */
return cell;
}