Introduction to Mantle & Overcoat for iOS & OSX

Limitless Virtual
5 min readFeb 16, 2015

--

Any experienced iOS & OSX developer knows that working with RESTful JSON web services can be cumbersome & complicated. From parsing responses to serializing for requests, it sometimes ain’t pretty.

In comes Mantle, a model layer framework written by the folks at GitHub which assists with mapping JSON to models and vice versa. Mantle is a wrapper for NSCoder which eliminates a lot of boiler plate code that we would usually need to implement.

The Model

Once you have setup Mantle within your project you can start creating your models. Imagine our JSON for a specific movie looked like this:

{
“id”: 1,
“title”: “Inception”,
“description”: “A thief who steals corporate secrets through use of dream-sharing technology is given the inverse task of planting an idea into the mind of a CEO.”,
“release_date”: “2010–07–16",
“rating”: 8.8
}

Our Mantle Model Class (Movie.h) will look like this:

#import <Mantle/Mantle.h>@interface Movie : MTLModel <MTLJSONSerializing>@property (nonatomic, copy, readonly) NSNumber *movieId;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *description;
@property (nonatomic, copy) NSDate *releaseDate;
@property (nonatomic, copy) NSNumber *rating;
@end

As you can see, all we do is subclass MTLModel and implement MTLJSONSerializing.

Now we need to specify in our implementation (Movie.m) how to map our JSON to the model:

+ (NSDictionary*)JSONKeyPathsByPropertyKey {
return @{
@”movieId”: @”id”,
@”title”: @”title”,
@”description”: @”description”,
@”releaseDate”: @”release_date”,
@”rating”: @”rating”
};
}

As you can see the dictionary keys (left) are our model properties and the values (right) are the JSON properties. In the event that we have a very_ugly_and_non_standard JSON key, we can map it a more readable and precise model property. E.g: Changing release_date to camel case.

Value Transforming

Mantle automatically handles strings and numbers but what about other data types? In our Movie class we have a releaseDate which is of type NSDate but the corresponding JSON is a string. Mantle provides functionality to transform values back and forth between types. We need to implement the following methods (Movie.m):

+ (NSDateFormatter*)dateFormatter {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @”yyyy-MM-dd”;
return dateFormatter;
}
+ (NSValueTransformer *)releaseDateJSONTransformer {
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *str) {
return [self.dateFormatter dateFromString:str];
} reverseBlock:^(NSDate *date) {
return [self.dateFormatter stringFromDate:date];
}];
}

First we created a method to return a NSDateFormatter with the correct date format. Secondly we created a method that specifies that we will use the above mentioned formatter on our releaseDate property.

The template to create a transformer for a property is as follows:

+ (NSValueTransformer*)propertyNameTransformer;

The JSON Adapter

Now that we have our Movie Model setup, how do we use it?

Let’s assume for now that we have pulled the JSON from our API using our favourite networking framework and parsed the JSON into a dictionary.

Using the MTLJSONAdapter class we can convert JSON to our Model:

Movie *movie = [MTLJSONAdapter modelOfClass:[Movie class] fromJSONDictionary:movieDictionary error:&error];

To convert our Model back to JSON:

NSDictionary *movieDictionary = [MTLJSONAdapter JSONDictionaryFromModel:movie];

Persistence

Our Movie Model adheres to NSCoder so we could store and fetch it:

//Store
[NSKeyedArchiver archiveRootObject:movie toFile:@"movie_file"];
//Fetch
Movie *fetchedMovie = [NSKeyedArchiver unarchiveObjectWithFile:@"movie_file"];

Although using the above methods are useful, the real strength of Mantle is that is works seamlessly with Core Data.

Core Data

We firstly create our Core Data Movie Entity as usual with the properties specified in our model.

Secondly we specify in our Model (Movie.h) that we will be implementing MTLManagedObjectSerializing:

#import <Mantle/Mantle.h>@interface Movie : MTLModel <MTLJSONSerializing, MTLManagedObjectSerializing>@property (nonatomic, copy, readonly) NSNumber *movieId;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *description;
@property (nonatomic, copy) NSDate *releaseDate;
@property (nonatomic, copy) NSNumber *rating;
@end

Lastly we specify in the model implementation (Movie.m) the entity name and the mappings:

+(NSString *)managedObjectEntityName {
return @”Movie”;
}
//We return an empty dictionary because the property names are identical in the class and entity
+ (NSDictionary *)managedObjectKeysByPropertyKey {
return @{};
}

The Managed Object Adapter

Now that we have everything setup, we can insert an existing Movie object into Core Data as so:

NSManagedObject *managedMovie = [MTLManagedObjectAdapter managedObjectFromModel:movie insertingIntoContext:moc error:&error];

To fetch a model from Core Data we fetch the managed object and then use the adapter to convert it to our Mantle model:

Movie *movie = [MTLManagedObjectAdapter modelOfClass:[Movie class] fromManagedObject:object error:&error];

The Scenario

Imagine we needed to pull a list of movies from our API thus we would need to do the following:

  • Execute the network request.
  • Create a new NSValueTransformer that uses the built in Mantle JSON array transformer for the Movie class.
  • Use the transformer on the response to create Movie objects.
  • Optional: Insert the objects into Core Data.

That’s potentially a lot of repeatable code (bold) that will need to be written for every request in our app:

//A dummy api client example
[apiClient GET:"movies" success:^(id response) {
NSArray *results = response.results;
NSValueTransformer *transformer;
transformer = [NSValueTransformer mtl_JSONArrayTransformerWithModelClass:[Movie class]];
NSArray* movies = [transformer transformedValue:results];

}];

On top of this, mapping could take along time and should be handled in a background queue.

Enter Overcoat.

Overcoat

Overcoat is a small but powerful library that makes creating REST clients simple and fun. It provides a simple API for making requests and mapping responses to model objects.

Overcoat is the perfect accessory to Mantle and is built on top of the popular AFNetworking framework.

Setup the Client

Firstly we need to create a client class that will consume our API which subclasses OVCHTTPSessionManager (APIClient.h):

#import <Overcoat/Overcoat.h>@interface APIClient : OVCHTTPSessionManager@end

Note: Applications targeting OS X 10.8 or iOS 6.0 must subclass OVCHTTPRequestOperationManager instead.

Secondly we need to map our API URL paths to our models. E.g: /movies and /movies/1 by implementing the following method (APIClient.m):

+ (NSDictionary *)modelClassesByResourcePath {
return @{
@”movies/*”: [Movie class]
};
}

You don’t need to specify the full path, and you can use * to match any text or # to match only digits.

Using the Client

Now that our client is setup, lets pull a list of movies as done before:

//Create our client
APIClient *apiClient = [[APIClient alloc] initWithBaseURL:[NSURL URLWithString:kAPIBaseUrl]];
//Fetch the movies
[apiClient GET:@”movies” parameters:nil completion:^(OVCResponse *response, NSError *error) {
NSArray *users = response.result; // Array of Movie objects!
}];

Thats it! No more boiler code on every request. By subclassing OVCHTTPSessionManager and implementing the required method, we can now call different URLs and Overcoat will automatically convert the response to our Mantle objects!

Core Data Integration

If you initialize the APIClient with a valid NSManagedObjectContext, Overcoat will automatically persist any models that were parsed from the response if the model implements MTLManagedObjectSerializing.

Conclusion & Further Reading

You now have the arsenal to create clean & powerful iOS applications that make consuming a RESTful API a breeze. The following points should allow for further reading:

  • Mantle provides functionality to parse JSON child objects and integrate them with existing relationships. E.g: Movies can have many Genres.
  • Overcoat allows you to cater for custom error and general responses.
  • Overcoat has support for PromiseKit & ReactiveCocoa

--

--