David Cordero

A tour of Mantle

Published on 28 Jun 2014

Mantle is most probably the best library nowadays for parsing JSON responses with Objective-C, but I am not going to say so much here about what is Mantle since that is not the proposal of this post.

There is already a quite good explanation about what Mantle is and what problems it solves on the oficial github page of the project. So I am not going to repeat the same things here.

The problems I had with Mantle were mainly related with the lack of example code in this project. Mainly about how to do specific things, so that is what I am going to explain here using some examples.

This is the JSON I will use for the next examples:

  {
    "book": {
      "title": "This is my book",
      "description": "This is an example of book to model",
      "published_in": "1378937453",
      "num_pages": 200,
      "author": {
        "name": "Pepito Perez",
        "age": 23
      }
    }
  }

We will start creating a simple Mantle Model for it:

  // Book.h
  
  #import <MTLModel.h>
  #import <MTLJSONAdapter.h>
  
  @interface Book : MTLModel
  
  @property (strong, nonatomic, readonly) NSString *title;
  @property (strong, nonatomic, readonly) NSString *description;
  @property (strong, nonatomic, readonly) NSDate *datePublication;
  @property (strong, nonatomic, readonly) NSNumber *numPages;
  
  @end

Implemented as:

  // Book.m

  #import "Book.h"

  #import <Mantle/MTLValueTransformer.h>
  #import <Mantle/NSDictionary+MTLManipulationAdditions.h>
  #import <Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.h>
  
  #pragma mark - MTLJSONSerializing
  + (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{
      @"title": @"title",
      @"description" : @"description",
      @"datePublication" : @"published_in",
      @"numPages", @"num_pages"
    };
  }

With the previous code the model is almost mapped, with the dictionary returned by JSONKeyPathsByPropertyKey each value of the JSON is mapped into a model property.

At this point we can start using the benefits of Mantle, because at this point our model has the methods isEqual, hash, copyWithZone, encodeWithCoder, initWithCoder, …

We could already create a new instance of Book with the following lines of code.

  [...]
  NSError *error;
  Book *myBook = [MTLJSONAdapter modelOfClass:[Book class]
                           fromJSONDictionary:jsonDictionary
                                        error:&error];
  [...]

Specific transformer for some keys

In some cases we could be interested in parse one of the keys using a custom transformer. It can be easily done defining a transformer for this key, as example we will create a specific parser for the key datePublication:

  + (NSValueTransformer *)datePublicationJSONTransformer {
    return [MTLValueTransformer reversibleTransformerWithBlock:^(NSString *str) {
      return [NSDate dateWithTimeIntervalSince1970:[str doubleValue]];
    }];
  }

Dot syntax for indicating the full path

What about the author information? It is inside a new envelope, so we need to do something special for that case. Actually we have two options for it.

The dot syntax allows to reach keys by indicating the full path.

  // Book.h

  [...]
  @property (strong, nonatomic, readonly) NSString *authorName;
  @property (strong, nonatomic, readonly) NSNumber *authorAge;
  [...]

Implemented as:

  // Book.m
  
  #pragma mark - MTLJSONSerializing
  + (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{
      @"title": @"title",
      @"description" : @"description",
      @"datePublication" : @"published_in",
      @"numPages", @"num_page",
      @"authorName", @"author.name",
      @"authorAge", @"author.age"
    };
  }

Submodel for a JSON subtree

Another option for modelling the author information is having a different model for it.

  //Author.h
  
  #import <MTLModel.h>
  #import <MTLJSONAdapter.h>
  
  @interface Author : MTLModel
  
  @property (strong, nonatomic, readonly) NSString *name;
  @property (strong, nonatomic, readonly) NSNumber *age;
  
  @end

Implemented as:

  // Author.m
  
  #pragma mark - MTLJSONSerializing
  + (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{
      @"name": @"name",
      @"age" : @"age"
    };
  }

And then we can indicate from Book.m that the author envelope has to be modeled with our new model Author:

  //Book.m

  [...]
  + (NSValueTransformer *)authorJSONTransformer {
    return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:Author.class];
  }
  [...]

Validation

Mantle also allows us to validate the model easily, as example we will validate our Book Model to check if it has defined a value for the key Title.

  // Book.h

  [...]
  static NSString * const BookModelError = @"BookModelError";
  static NSInteger const BookModelErrorMissingTitle = 1;
  [...]

Implemented as:

  // Book.m

  [...]
  #pragma mark - MTLValidationModel
  - (BOOL)validateTitle:(NSString **)value error:(NSError **)error {
    if (*value != nil) {
      return YES;
    }
    if (error != NULL) {
      *error = [NSError errorWithDomain:BookModelError
                                   code:BookModelErrorMissingTitle
                               userInfo:nil];
    }
    return NO;
  }
  [...]