Using Mantle with Core Data

July 17, 2013

I recently started using Mantle in some new projects. I had been following the framework for awhile and watching the Core Data adapter pull request. That pull request was recently merged and I’ve since been using Mantle to create Core Data entities using JSON from an HTTP response. This makes it really easy to take JSON and transform into managed objects persisted in your object store. Here’s how I am doing it.

The model objects are subclasses of MTModel and declare properties based on the JSON response respresented. Each MTModel subclass also implements the MTLJSONSerializing and MTLManagedObjectSerializing protocols.

@interface MFMarketModel : MTLModel <MTLJSONSerializing, MTLManagedObjectSerializing>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;

@property (nonatomic, copy) NSDate *startDate;
@property (nonatomic, copy) NSDate *endDate;
@property (nonatomic, copy) NSArray *daysOfWeek;

@property (nonatomic, copy) CLLocation *location;

@end

Each MTLModel subclass then implements JSONKeyPathsByPropertyKey and returns an NSDictionary of JSON attributes names to map onto properties. I also implemented some helper methods to return NSValueTransformer instances that are used for handling NSArray, NSDate, and CLLocation transforms. All standard stuff for Mantle.

For the Core Data magic to work, the method managedObjectKeysByPropertyKey needs to be implemented. If your entity has any relationships, then you also must implement relationshipModelClassesByPropertyKey. Both of these methods should return an NSDictionary of property and entity name pairs. One hangup I ran into was that any properties that I didn’t want to map onto entity attributes needed to be specified using [NSNull null].

+ (NSDictionary *)managedObjectKeysByPropertyKey {
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    
    [dictionary setObject:@"name" forKey:@"name"];
    [dictionary setObject:@"address" forKey:@"address"];
    [dictionary setObject:@"days" forKey:@"daysOfWeek"];
    
    [dictionary setObject:@"startDate" forKey:@"startDate"];
    [dictionary setObject:@"endDate" forKey:@"endDate"];
    [dictionary setObject:@"location" forKey:@"location"];
    
    // ignore these two properties
    [dictionary setObject:[NSNull null] forKey:@"hoursOfOperation"];
    [dictionary setObject:[NSNull null] forKey:@"datesOfOperation"];
    
    return dictionary;
}

You’ll need to specify the managed object entity name so Core Data knows what entity to return. This is accomplished by implementing managedObjectEntityName.

+ (NSString *)managedObjectEntityName {
    return @"MFManagedMarket";
}

Check out the MTLManagedObjectAdapter.h header file; it is well documented and contains a few other methods you may want to implement.

Once everything is setup, parsing JSON objects out of an NSDictionary and into and NSManagedObject looks a little like this:

NSArray *markets = [json valueForKeyPath:@"markets"];
[markets enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
    MFMarketModel *market = [MTLJSONAdapter modelOfClass:[MFMarketModel class]
                                      fromJSONDictionary:obj
                                                   error:NULL];
    NSManagedObject *managedMarket = [MTLManagedObjectAdapter managedObjectFromModel:market
                                                                insertingIntoContext:self.masterManagedObjectContext
                                                                               error:NULL];
}];

[self saveMasterManagedObjectContext];