Making Core Data usage saner

Starting with Core Data

Since I started iOS development after 3.0 was released I never knew anything else for data persisting.

What I did not like in all the samples seen: sending NSManagedObjectContext through application and repeating a lot of similar logic in multiple places. Fetching objects, counting, saving. And all error handling was also duplicated. I also had the pleasure to work on a project that did use this pattern. It was a mess.

The first attempt

Right at the start I wrapped managed object context inside ‘Database’ object and that one was passed through application. This object would also contain only version of methods needed for saving and objects fetching. All data access logic was contained in one place.

But this approach was also not without it’s problems. As project size and data model grew, also the database object was growing into unmanageable size. Database class was over 2000 lines and it was getting harder to make logical separation for different entities access.

Making the split

When I started on PokerLoot I had an idea formed. What if I could separate common core data manipulation from specific entities access. There would still be Database object containing the most basic fetches. And on another layer we would have code for manipulating entities.

This is where Objective-C’s categories came to rescue. I created a base class with common functionality:

@interface Database : NSObject {
 @private
  NSManagedObjectContext *managedObjectContext_;
  NSManagedObjectModel *managedObjectModel_;
  NSPersistentStoreCoordinator *persistentStoreCoordinator_;
}
- (void)saveContext;
- (NSManagedObjectContext *)managedObjectContext;
- (int)countCoreObjectsNamed:(NSString *)modelName;
- (int)countCoreObjectsNamed:(NSString *)modelName withPredicate:(NSPredicate *)predicate;
- (id)findCoreObjectNamed:(NSString *)modelName withPredicate:(NSPredicate *)predicate;
- (NSArray *)listCoreObjectsNamed:(NSString *)modelName;
- (NSArray *)listCoreObjectsNamed:(NSString *)modelName withPredicate:(NSPredicate *)predicate;
- (void)deleteObject:(NSManagedObject *)object;
@end

And for individual entities access I would have:

@interface Database (Venues)
- (NSFetchedResultsController *)fetchedResultsControllerForVenues;
- (Venue *)createVenueWithName:(NSString *)venueName iconName:(NSString *)iconName;
- (Venue *)createVenueWithCode:(NSString *)code venueName:(NSString *)venueName iconName:(NSString *)iconName;
- (Venue *)loadVenueWithCode:(NSString *)code;
@end

This has grouped code into smaller and better manageable chunks. And has created base code for Core Data access, that can be dropped into any new project.

Beyond access

When talking about Core Data, then one project should definitely also be mentioned - mogenerator.

Problem with entity classes generated by xcode - they will always get overwritten. Thus any changes there will be lost. If you just want to add new functions without actual object extension, the categories should be fine. But what if you also need to extend the object by adding new variables? This is where mogenerator comes to rescue. It will generate two set of classes for every entity. One that would always be regenerated on every model update (same thing as classes generated by xcode). And another class that can be freely modified and extended.

Tags: ios, core data