7 Comments

Simplify iOS Models With Mantle – An Intro

Mantle is a framework that makes using iOS models ridiculously easy. It helps manage all the boilerplate associated with models including serialization, deserialization, and equality.

Let’s say we have an application that needs to create a user and it must contact a server to do so. Also, let’s say the server is written in Ruby.

  1. Create the Model
  2. First, we need a User model to pass to the server. Here is what that model might look like:

    @interface EKOCloudAPIProfile : NSObject
     
    @property (strong, nonatomic, readwrite) NSString *firstName;
    @property (strong, nonatomic, readwrite) NSString *lastName;
    @property (strong, nonatomic, readwrite) NSNumber *age;
    @property (strong, nonatomic, readwrite) NSNumber *weight;
    @property (strong, nonatomic, readwrite) NSDate *birthdate;
     
    - (BOOL)isEqual:(id)anObject;
    - (void)hash;

    If we want to test our model later (of course we do), we should also implement -isEqual:, which of course requires us to also implement -hash. Yay, more boilerplate code! It is really going to get tedious implementing that -isEqual: method with all of the properties we have.

    Luckily, Mantle has MTLModel. All of our models should inherit from MTLModel. Oh, by the way, MTLModel implements -isEqual:, -hash, and all of the methods from NSCopying for us. Here is our MTLModel:

    @interface EKOCloudAPIProfile : MTLModel <MTLJSONSerializing>
     
    @property (strong, nonatomic, readwrite) NSString *firstName;
    @property (strong, nonatomic, readwrite) NSString *lastName;
    @property (strong, nonatomic, readwrite) NSNumber *age;
    @property (strong, nonatomic, readwrite) NSNumber *weight;
    @property (strong, nonatomic, readwrite) NSDate *birthdate;
     
    @end

  3. Set Up Property Keys
  4. Now that we have our MTLModel, we need to implement the only required method; +JSONKeyPathsByPropertyKey. This method is responsible for serializing variables in the casing the server wants them, and deserializes them into the properties we want. It’ll look something like this:

    + (NSDictionary)JSONKeyPathByPropertyKey {
        return @{
            @"firstName" : @"first_name",
            @"lastName" : @"last_name"
        };
    }

    Since age, weight, and birthdate will look the same on both sides, we can emit them from the dictionary.

  5. Add Property Transformations
  6. If we stopped here, our serialized JSON would contain an NSDate object for the birthdate. Another goodie from Mantle is custom JSON transformer methods. We can add custom serialization or deserialization for any property by using +<key>JSONTransformer.

    We really want to serialize the birthdate property into a string, and we would expect the server to send us a string that we will then deserialize back into an NSDate. Here is our implementation:

    + (NSValueTransformer *)birthdateJSONTransformer 
        dateFormatter = [[NSDateFormatter alloc] init];
        dateFormatter.dateFormat = @"yyyy/MM/dd";
     
        return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *dateStr) {
            return [dateFormatter dateFromString:dateStr];
        } reverseBlock:^(NSDate *date) {
            return [dateFormatter stringFromDate:date];
        }];
    }

    Using +reversibleTransformerWithForwardBlock:reverseBlock: from MTLValueTransformer, we can serialize a property with forwardBlock and deserialize with reverseBlock.

  7. Use the Model
  8. Now that our model is looking all pretty, it is time to actually use it!

    When we want to serialize our model to send in a request we use [MTLJSONAdapter +JSONDictionaryFromModel:].

    When we want to deserialize our model from a response we use [MTLJSONAdapter +modelOfClass:fromJSONDictionary:error:].

    Here is what our network layer might look like:

    - (User)updateUser:(User)user {
        NSDictionary *userDict = [MTLJSONAdapter JSONDictionaryFromModel:user];
        NSDictionary *updatedUser = [self.webClient postToPath:@"users.json" withParameters:userDict];
     
        return [MTLJSONAdapter modelOfClass:[User class] fromJSONDictionary:updatedUser error:nil];
    }

And that’s it! We have a model that can be easily serialized, deserialized, and compared to other objects. Be sure to checkout Mantle’s Github page for plenty more treats.