iOS Mantle + Overcoat

2.5k views Asked by At

I'm looking at the Overcoat library which from what I gather is a library that extends the Mantle library.

Mantle: https://github.com/Mantle/Mantle/ Overcoat: https://github.com/gonzalezreal/Overcoat

The Mantle and Overcoat github pages keeps mentioning about creating a Mantle model but I want to know how do I generate the Mantle model? Do I manually type it out or do I use Xcode xcdatamodel file to build it visually, then generate the sublass + modify that file afterwards?

In Core Data do create the entity in the xcdatamodel file using the Interface Builder, then use Xcode's Editor > Create NSManagedObject subclass.

Do we do the same for Mantle and then change from NSManagedObject to MTLModel ?

What happens when we decided to update the Core Data entity in the xcdatamodel file? If we regenerate the model file again, wouldn't we have to re-add all those changes to the NSManagedObject class?

Super confused about the process.

1

There are 1 answers

0
Zhang On

OK, I think I'm starting to grasp it a bit more. After a couple of hours of trial and error, I was able to get a basic Overcoat demo app working with Core Data pulling from my REST API.

I got it to work like so:

1) We create the Entities inside the xcdatamodel file BUT DO NOT generate the NSManagedObject class using Editor > Create NSManagedObject Classes menu.

2) Create the Mantle model subclass per the normal Right Click project folder (in Xcode) > New File > Choose MTLModel as subclass and then manually entering in the properties. Notably, the subclass header should be something like:

@interface Book : MTLModel <MTLJSONSerializing, MTLManagedObjectSerializing> 

@property (nonatomic, copy) NSString *title;
...

@end

3) If you accidentally generated the Core Data entity like me, the xcdatamodel file actually adds the class name in the "Default" section under "Configuration" inside the xcdatamodel.

You need to delete any value in the "Class" column otherwise you end up with a bad crash saying:

"XYZ" is not a subclass of NSManagedObject.

4) Ensure in your Mantle model class you implement the serializing methods for MTLJSONSerialization and MTLManagedObjectSerializing.

#pragma mark - MTLJSONSerialization -

+(NSDictionary *)JSONKeyPathsByPropertyKey
{    
    return @{
             @"title": @"title",
             ...
             };
}

#pragma mark - MTLManagedObjectSerializing -

+(NSString *)managedObjectEntityName
{
    // ------------------------------------------------
    // If you have a Core Data entity called "Book"
    // then you return @"Book";
    //
    // Don't return the Mantle model class name here.
    // ------------------------------------------------
    return @"TheCoreDataEntityName";
}

+(NSDictionary *)managedObjectKeysByPropertyKey
{
    // ------------------------------------------------
    // not really sure what this does, I just put 
    // it in as the example does it too
    // ------------------------------------------------
    return @{};
}

These methods essentially is the glue mapping the JSON response from the server to the Core Data Entities.

5) One more important thing that got me is the way the server return responses.

Your server might use HTTP Status code and no top level JSON dictionary

e.g.

// no top level JSON dictionary, purely just an array of results
{
    {
        title: "ABC",
        ...
    },
    {
        title: "ABC",
        ...
    },
    {
        title: "ABC",
        ...
    },
}

Whereas, other types of REST server might return a top level JSON dictionary with the results key path at a sub level like so:

{
    count: 20,
    next: "http://www.server.com/api/resource?page=2",
    previous: null,
    results:(
    {
        title: "ABC",
        ...
    },
    {
        title: "ABC",
        ...
    },
    {
        title: "ABC",
        ...
    })
}

In the latter case, that's known as an "Envelop" type of response from my vague understanding. For these type of server responses, there is an extra step that involves telling Overcoat where the array of results key path is in the JSON response.

To do this, we need to:

5a) Create a ServerResponse class that is a subclass of OVCresponse:

// .h file
#import "OVCResponse.h"

@interface ServerResponse : OVCResponse

@end

// .m file
@implementation ServerResponse

+(NSString *)resultKeyPathForJSONDictionary:(NSDictionary *)JSONDictionary
{
    // --------------------------------------------------------------------
    // we're telling Overcoat, the array of entities is found under the
    // "results" key-value pair in the server response JSON dictionary
    // --------------------------------------------------------------------
    return @"results";
}

@end

5b) In your APIClient class (which should be a subclass of OVCHTTPSessionManager), override the method:

+(Class)responseClass
{
    // --------------------------------------------------
    // ServerResponse class will let Overcoat know 
    // where to find the results array
    // --------------------------------------------------
    return [ServerResponse class];
}

Hopefully this helps anyone else who's having the same problem trying to get Overcoat working.