iosobjective-crestkitrestkit-0.20

RestKit Add Property Mapping and Relationship Mapping


Please guide me about following problem.

I have two entities with relationship as shown following image enter image description here

I am using latest version of RestKit with iOS 7

Now in my appDelegate i am using following mapping for "List" Entity

    NSDictionary *listObjectMapping = @{
                                      @"listID" : @"listID",
                                      @"listName" : @"listName",
                                      @"listSyncStatus" : @"listSyncStatus"
                              };

RKEntityMapping *listEntityMapping = [RKEntityMapping mappingForEntityForName:@"List" inManagedObjectStore:managedObjectStore];
[listEntityMapping addAttributeMappingsFromDictionary:listObjectMapping];

listEntityMapping.identificationAttributes = @[ @"listID" ];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:listEntityMapping
                                                                                        method:RKRequestMethodGET
                                                                                   pathPattern:@"/api/lists"
                                                                                       keyPath:nil
                                                                                   statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];


//Inverse mapping, to perform a POST
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[listEntityMapping inverseMapping]
                                                                               objectClass:[List class]
                                                                               rootKeyPath:nil
                                                                                    method:RKRequestMethodPOST];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];

[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:@"application/json"];
[objectManager addRequestDescriptor:requestDescriptor];



//Inverse mapping, to perform a PUT
requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[listEntityMapping inverseMapping]
                                                                               objectClass:[List class]
                                                                               rootKeyPath:nil
                                                                                    method:RKRequestMethodPUT];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];

[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:@"application/json"];
[objectManager addRequestDescriptor:requestDescriptor];

and using following mapping for my Task object

    NSDictionary *taskObjectMapping = @{
                                    @"listID" : @"listID",
                                    @"taskID" : @"taskID",
                                    @"taskName" : @"taskName",
                                    @"taskCompletionStatus" : @"taskCompletionStatus",
                                    @"taskSyncStatus" : @"taskSyncStatus"
                                };

RKEntityMapping *taskEntityMapping = [RKEntityMapping mappingForEntityForName:@"Task" inManagedObjectStore:managedObjectStore];
[taskEntityMapping addAttributeMappingsFromDictionary:taskObjectMapping];
taskEntityMapping.identificationAttributes = @[ @"taskID" ];


RKResponseDescriptor *taskResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:taskEntityMapping
                                                                                            method:RKRequestMethodGET
                                                                                       pathPattern:@"/api/list/:id"
                                                                                           keyPath:nil
                                                                                       statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:taskResponseDescriptor];


//Inverse mapping, to perform a POST
RKRequestDescriptor *taskRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[taskEntityMapping inverseMapping]
                                                                               objectClass:[Task class]
                                                                               rootKeyPath:nil
                                                                                    method:RKRequestMethodPOST];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:@"application/json"];
[objectManager addRequestDescriptor:taskRequestDescriptor];



//Inverse mapping, to perform a PUT
taskRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[taskEntityMapping inverseMapping]
                                                          objectClass:[Task class]
                                                          rootKeyPath:nil
                                                               method:RKRequestMethodPUT];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:@"application/json"];
[objectManager addRequestDescriptor:taskRequestDescriptor];

Now my question is how to add relationship mapping between these two entites ? What would be proper way ?

If i use i use following line of code

[taskEntityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"list.listID"
                                                                                  toKeyPath:@"listID"
                                                                                withMapping:listEntityMapping]];

a runtime error occurs saying "Unable to add mapping for keyPath listID, one already exists"

and if i use this

[listEntityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"listID"
                                                                                      toKeyPath:@"list.listID"
                                                                                    withMapping:taskEntityMapping]];

app crashes with this error the entity List is not key value coding-compliant for the key "list".'

and if i omit the above code and try to get listID from relationship

task.list.listID 

it gives me "0". Can anyone tell me what exactly am i doing wrong or what should i do to accomplish above task. I can give more details on this if needed.

EDIT

but my request to all list returns following json

GET www.mydomain.com/api/lists

[
{"listID":"42","listName":"List 4","listSyncStatus":"1"},
{"listID":"41","listName":"List 3","listSyncStatus":"1"},
{"listID":"40","listName":"List 2","listSyncStatus":"1"}
]

and request to single list will return its task as follows

GET www.mydomain.com/api/list/42
[
{"taskID":"22","listID":"42","taskName":"Task 2","taskSyncStatus":"1","taskCompletionStatus":"1"},

{"taskID":"21","listID":"42","taskName":"Task 1","taskSyncStatus":"1","taskCompletionStatus":"1"}
]

i.e there is no cascading relationship in returned in json. is this wrong way or what am i missing here ?

Corrected After Accepting Answer

It turns out i was returning wrong json i.e. the returned json has no relationship in it while the iOS model has a relationship "tasks" so i edited my rest api to return correct nested json which is like below

    [ { "listID" : "96",
    "listName" : "List 1",
    "listSyncStatus" : "1",
    "tasks" : [ { "taskCompletionStatus" : "1",
          "taskID" : "67",
          "taskName" : "Task 2",
          "taskSyncStatus" : "1"
        },
        { "taskCompletionStatus" : "1",
          "taskID" : "66",
          "taskName" : "Task 1",
          "taskSyncStatus" : "1"
        }
      ]
  },
  { "listID" : "97",
    "listName" : "List 2",
    "listSyncStatus" : "1",
    "tasks" : [ { "taskCompletionStatus" : "1",
          "taskID" : "69",
          "taskName" : "Task 1",
          "taskSyncStatus" : "1"
        },
        { "taskCompletionStatus" : "1",
          "taskID" : "68",
          "taskName" : "Task 1",
          "taskSyncStatus" : "1"
        }
      ]
  }
]

after returning above nested json, everything works like charm, specially this line

[listEntityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"tasks"
                                                                                  toKeyPath:@"tasks"
                                                                                withMapping:taskEntityMapping]];

Hope this helps grasping relationship concepts for people like me out there.


Solution

  • You modal diagram shows that you have one-to-many relationship (One list has many tasks).

    As far as i know, in this case you need to add relationship mapping on List entity only, no need on Task entity. Also for one-to-many relationship, you don't need to add list relationship under Task entity.

    Your entities relationship should look like this

    enter image description here

    So try following

    [listEntityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"YOUR_JSON_KEYPATH_HERE" toKeyPath:@"tasks" withMapping: taskEntityMapping]];
    

    IMPORTANT

    In above method

    FromKeyPath parameter should be the name of your JSON key where the relationship starts.

    toKeyPath parameter should be the relationship name that you have mentioned in Entity diagram. i.e; tasks.

    withMapping should be the mapping of many entity. In you case taskEntityMapping

    Hope this fix the issue.