iosjsoncore-datarestkitrestkit-0.20

Restkit map the response to a parent Entity - when response does not have parent entity values


I'm using RestKit 0.2 version in my project. I have a situation where I need to map the response to an already saved it's parent entity.

My Parent Class is Conversation & conversation can have many messages.

First I'm saving the all the conversations in Coredata under Conversation entity.

Then I called this url for get all the messages related to a specific conversations.

http://myUrl/conversations/bdc34e2a-fa1f-4dbe-83b4-3296ff657e93/messages

bdc34e2a-fa1f-4dbe-83b4-3296ff657e93 is my conversation id

Here is my json response.

[  
 {  
  "id":"5f67f6f5-934d-403e-ac64-19dbd4defeda",
  "sent_at":"2016-06-08T12:24:28+0000",
  "read_at":null,
  "sender":{  },
  "content":"test message",
  "attachment":{  }
 },
 {  
  "id":"c4d18b33-4c54-4e7e-a964-a5cedc087122",
  "sent_at":"2016-06-08T09:59:41+0000",
  "read_at":null,
  "sender":{  },
  "content":"abc test message",
  "attachment":{  }
 },

My Json response does not have the conversation property, but I need to map all my messages under the relevant conversation.

RKEntityMapping *messageMapping = [RKEntityMapping mappingForEntityForName:[Message entityName] inManagedObjectStore:[[DataStoreManager sharedManager] managedObjectStore]];

[messageMapping addAttributeMappingsFromDictionary:@{
                                                     @"id":@"identifier",
                                                     @"sent_at": @"date",
                                                     @"content": @"message",

                                                    }];

messageMapping.identificationAttributes = @[@"identifier"];

I have many to one relationship for messages to conversation.

[messageMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:@"conversation" withMapping:**conversationMapping**]];

But how can I create conversationMapping ?

Edited - Trying with routing & meta data

As @Wain suggested , I'm trying to use restkit routing

// conversation mapping
RKEntityMapping *conversationMapping = [RKEntityMapping mappingForEntityForName:[Conversation entityName] inManagedObjectStore:[[BADataStoreManager sharedManager] managedObjectStore]];

[conversationMapping addAttributeMappingsFromDictionary:@{
                                                          @"@metadata.routing.parameters.identifier": @"identifier"

                                                          }];


conversationMapping.identificationAttributes = @[@"identifier"];

[messageMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:@"messages" withMapping:conversationMapping]];

here is my response descriptor

RKResponseDescriptor *conversationDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[EntityMappingProvider messagesResponseMapping] method:RKRequestMethodAny pathPattern:@"/conversations/:identifier/messages" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

[[DataStoreManager sharedManager].router.routeSet addRoute:[RKRoute routeWithName:@"conversations" pathPattern:@"/conversations/:identifier/messages" method:RKRequestMethodGET]];

[[DataStoreManager sharedManager] addResponseDescriptor:conversationDescriptor];

[[DataStoreManager sharedManager] getObjectsAtPathForRouteNamed:@"conversations" object:conversation parameters:nil
                                                         success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                                             RKLogInfo(@"response %@",mappingResult);
                                                         }

                                                         failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                                             RKLogInfo(@"error response %@", error.localizedDescription );
                                                         }];

object:conversation will be sent when user select one conversation from the conversation list.

But still conversation are not correctly mapped to the messages.

& I can call this method only once, then I'm getting the following error .

reason: 'Cannot add a route with the same name as an existing route.'

but it should be usable to all the conversations , whenever user selects a conversation this method will be called.

Solution

I have used response descriptors to map conversations & users before mapping messages in the conversations. So when I used the response descriptor for the messages in the conversations, it was mapped to previously defined descriptors.

So I had to use pathPattern & specify the response descriptors , then it mapped correctly , as @Wain explained , I have initialized all the response descriptors once & use getObjectsAtPathForRouteNamed several times.

Finally here is my conversation mapping & it's response descriptor

// conversation mapping
RKEntityMapping *conversationMapping = [RKEntityMapping mappingForEntityForName:[Conversation entityName] inManagedObjectStore:[[BADataStoreManager sharedManager] managedObjectStore]];
[conversationMapping addAttributeMappingsFromDictionary:@{
                                                          @"@metadata.routing.parameters.identifier": @"identifier"

                                                          }];
conversationMapping.identificationAttributes = @[@"identifier"];
[messageMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:@"messages" withMapping:conversationMapping]];

// Response descriptor 
RKResponseDescriptor *conversationDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[EntityMappingProvider messagesResponseMapping] method:RKRequestMethodAny pathPattern:@"/conversations/:identifier/messages" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[DataStoreManager sharedManager].router.routeSet addRoute:[RKRoute routeWithName:@"conversations" pathPattern:@"/conversations/:identifier/messages" method:RKRequestMethodGET]];
[[DataStoreManager sharedManager] addResponseDescriptor:conversationDescriptor];
[[DataStoreManager sharedManager] getObjectsAtPathForRouteNamed:@"conversations" object:conversation parameters:nil
                                                         success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                                             RKLogInfo(@"response %@",mappingResult);
                                                         }
                                                              failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                                             RKLogInfo(@"error response %@", error.localizedDescription );
                                                         }];

Solution

  • You should really use a route so that you can use a path pattern to insert the conversation id. Then you can use the mapping metadata to access the id value in the mapping and use it to connect to the appropriate object.