blog

GitHub’s Mantle for Cocoa and Cocoa Touch

by

Octocat logoEarlier this week GitHub announced Mantle, a model framework for Objective-C that is lighter weight than Core Data and well suited to implementing a model layer for an app that interacts with web services. The project is available on GitHub and also as a Cocoapods pod and works with iOS and Mac OS X apps.

I’ve been working on an Objective-C implementation of the Cosm API using the excellent AFNetworking framework with model objects as simple NSObject subclasses. After reading about Mantle I wanted to take a look at how much work I could save myself so updated one of my model objects to be an MTLModel subclass instead.

Before…

@implementation CBCosmDatastream
- (id)initWithAttributes:(NSDictionary*)attributes
{
   self = [super init];
   if (self)
   {
      if (!attributes)
      {
         return nil;
      }
      for (NSString* key in attributes)
      {
         [self setValue:[attributes valueForKey:key]
                 forKey:key];
      }
   }
   return self;
}
#pragma mark - Equality
- (NSUInteger)hash
{
   return [self.datastreamId hash];
}
- (BOOL)isEqual:(id)object
{
   if ([object isKindOfClass:[CBCosmDatastream class]])
   {
      CBCosmDatastream* otherDatastream = (CBCosmDatastream*)object;
      return [self.datastreamId isEqualToNumber:otherDatastream.datastreamId];
   }
   else
   {
      return NO;
   }
}
#pragma mark - KVC
- (void)setValue:(id)value forKey:(NSString*)key
{
   if ([key isEqualToString:@"id"])
   {
      [super setValue:value forKey:@"datastreamId"];
   }
   else if ([key isEqualToString:@"at"])
   {
      [super setValue:value forKey:@"updated"];
   }
   else if ([key isEqualToString:@"current_value"])
   {
      [super setValue:value forKey:@"value"];
   }
   else if ([key isEqualToString:@"min_value"])
   {
      [super setValue:value forKey:@"minValue"];
   }
   else if ([key isEqualToString:@"max_value"])
   {
      [super setValue:value forKey:@"maxValue"];
   }
   else if ([key isEqualToString:@"datapoints"])
   {
      if ([value isKindOfClass:[NSArray class]])
      {
         NSArray* values = (NSArray*)value;
         NSMutableArray* datapoints = [NSMutableArray arrayWithCapacity:[values count]];
         for (NSDictionary* attributes in values)
         {
            CBCosmDatapoint* datapoint = [[CBCosmDatapoint alloc] initWithAttributes:attributes];
            [datapoints addObject:datapoint];
         }
         _datapoints = datapoints;
      }
   }
}
@end

and after…

@implementation CBCosmDatastream
#pragma mark - Mantle
+ (NSDictionary *)externalRepresentationKeyPathsByPropertyKey
{
   return [super.externalRepresentationKeyPathsByPropertyKey mtl_dictionaryByAddingEntriesFromDictionary:@{
           @"datastreamId": @"id",
           @"updated": @"at",
           @"value": @"current_value",
           @"minValue": @"min_value",
           @"maxValue": @"max_value"
           }];
}
#pragma mark - KVC
- (void)setValue:(id)value forKey:(NSString*)key
{
   if ([key isEqualToString:@"datapoints"])
   {
      if ([value isKindOfClass:[NSArray class]])
      {
         NSArray* values = (NSArray*)value;
         NSMutableArray* datapoints = [NSMutableArray arrayWithCapacity:[values count]];
         for (NSDictionary* attributes in values)
         {
            CBCosmDatapoint* datapoint = [[CBCosmDatapoint alloc] initWithAttributes:attributes];
            [datapoints addObject:datapoint];
         }
         _datapoints = datapoints;
      }
   }
}
@end

I was able to remove most of my boilerplate code and will be able to replace the setValue:forKey: method with a NSValueTransformer after updating the CBCosmDatapoint class to be an MTLModel subclass. I replaced the initWithAttributes: method calls in my unit tests with ones to initWithDictionary: and passed all my tests without any other changes with just a few minutes of work. I like having less code to maintain and will definitely be updating the rest of my model objects.

Mantle is early days at only version 0.2 but a project definitely to keep an eye on for your iOS and OS X apps.

+ more