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

Accurate Timing

Accurate Timing

In many tasks we need to do something at given intervals of time. The most obvious ways may not give you the best results. Time? Meh. The most basic tasks that don't have what you might call CPU-scale time requirements can be handled with the usual language and...

read more