0

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 );
                                                         }];
Udaya Sri
  • 2,372
  • 2
  • 25
  • 35

1 Answers1

1

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.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • See this answer http://stackoverflow.com/questions/18324033/map-url-parameters-to-objects-using-restkit – Wain Jun 21 '16 at 10:17
  • I tried your suggestion , but still I have no luck. Can you please look in to my editing ? – Udaya Sri Jun 21 '16 at 16:07
  • how often are you calling `addRoute`? you should set up all your routes and mappings once at the start of your app and not do it repeatedly – Wain Jun 21 '16 at 16:11
  • but how can I do it . There can be new conversations & users can send new messages , so users have to go in to the conversations to see the messages . So I can not tell any exact amount of time. This method will call every time, user select one conversation from the list. – Udaya Sri Jun 21 '16 at 16:14
  • the configuration of the object manager should be unrelated to your view controllers. a bad implementation would do it in the app delegate, a good solution would have a custom specific class for it – Wain Jun 21 '16 at 16:24
  • Yes I have a separate class for the objectmanager. DataStoreManager is that class. It handles the initiation of manageobjectstore & context etc. sender mapping & message mapping is working correctly. Only thing I'm unable to map is the conversation to the message. As I understood I can not use routing in here ,becuase I need to call this method when user select one conversation from the list. So is there any other way to handle that ? – Udaya Sri Jun 21 '16 at 16:33
  • the descriptor and route are generic, add them once, only call `getObjectsAtPathForRouteNamed` multiple times – Wain Jun 21 '16 at 19:18
  • I added the descriptor & route once & called getObjectsAtPathForRouteNamed method , but then it's not attaching to that descriptors , it does not call the mapping :( – Udaya Sri Jun 21 '16 at 21:37
  • Ok I'll edit the question & add the RKobjectManager class , entitymapping & request calling class ,, – Udaya Sri Jun 21 '16 at 21:44
  • I have edited the question , can you please check it . Thank you so much for your time & help. I really appreciate it. This is my first time with RestKit & Coredata – Udaya Sri Jun 21 '16 at 22:03
  • You don't need to call shared manager when in the manager, other than that it looks ok,Mehta does RestKit log? Turn on trace logging – Wain Jun 21 '16 at 22:25
  • Thanks a lot for your help, it worked :) I used same descriptors without using path patterns,so they were messing up. .. – Udaya Sri Jun 23 '16 at 08:08