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;
}
[...]