I'm just starting to use Restkit and am trying to send a regular POST request "serverUrl/account/authenticate.xml?OPTIONS" to get a response as XML. This is the code I use to call the URL:
-(void) function{
// OBJECT MANAGER
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:serverUrl];
[manager.router routeClass:[CBUser class] toResourcePath:#"/account/authenticate" forMethod:RKRequestMethodPOST];
// OBJECT MAPPING
RKObjectMapping* mapping = [RKObjectMapping mappingForClass:[CBUser class]];
mapping = [manager.mappingProvider objectMappingForKeyPath:#"user"];
[mapping mapKeyPath:#"id" toAttribute:#"userId"];
[manager loadObjectsAtResourcePath:str objectMapping:mapping delegate:self];
}
// DELEGATE
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
CBUser* user = [objects objectAtIndex:0];
NSLog(#"Loaded Contact ID #%# -> Firstname: %#, Lastname: %#", user.userId, user.firstname, user.lastname);
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {
NSLog(#"Encountered an error: %#", error);
}
Looking at the server log it seems it receives a GET request instead of a POST even though I'm using the "RKRequestMethodPOST" option.
Here is my error form my logs:
Started GET "/account/authenticate.xml?commit=Login&authenticity_token=iPhone&user%5Bpassword=XXXXX&user%5Bemail=XXXXXXX%5D&user%5Bdevice_token=XXXXXXX" for 192.168.106.30 at 2012-01-25 19:20:03 -0800
AbstractController::ActionNotFound (The action 'show' could not be found for AccountController):
Rendered /Users/Guillaume/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.0.9/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb within rescues/layout (1.2ms)
What am I missing?
Hopefully this code snippet of my app will help. You setup your mappings, say in your delegate. Note the "forMethod"
RKObjectRouter *router;
RKObjectMapping* createAccountSerializationMapping = [RKObjectMapping mappingForClass:[Cr eateAccount class]];
[createAccountSerializationMapping mapAttributes:#"email", #"pwd", #"uname", nil];
[[RKObjectManager sharedManager].mappingProvider setSerializationMapping:createAccountSerializationMapping forClass:[CreateAccount class]];
router = [RKObjectRouter new] ;
[router routeClass:[CreateAccount class] toResourcePath:#"/registration/rest/users/create_account" forMethod:RKRequestMethodPOST];
[RKObjectManager sharedManager].router = router;
and then later, when you want to post an object
[[RKObjectManager sharedManager] postObject:user delegate:self];
Related
I have a special need to send a pre-formatted JSON string to the server. Due to the server using older Microsoft technology the JSON elements MUST be in a certain order. If I use the standard JSON processing of RestKit the JSON elements come from a dictionary and are added in hash order. Sadly this will not work for this one special case.
How can I send a pre-formatted JSON string instead of an NSDictionary that is converted to a JSON string with RestKit 0.2x?
Here is the code for the request using NSDictionary
RKObjectManager *objectManager = self.createObjectManager;
RKObjectMapping *requestMapping = [EssenceRequest.objectMapping inverseMapping];
[objectManager addRequestDescriptor:[RKRequestDescriptor requestDescriptorWithMapping:requestMapping
objectClass:EssenceRequest.class
rootKeyPath:nil
method:RKRequestMethodPOST]];
RKObjectMapping *responseMapping = EssenceRoot.objectMapping;
RKResponseDescriptor* essenceResponse = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
method:RKRequestMethodPOST
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:essenceResponse];
EssenceRequest *dataObject = [[EssenceRequest alloc] initWithContextAndHandle:uniqueHandle essenceHandle:essenceHandle];
[objectManager postObject:dataObject
path:[NSString stringWithFormat:#"%#%#%#GetEssences", Connection.apiPrefix, Connection.svcMedia, Connection.jsonSecure]
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[serverResponseDelegate serverResponseSuccess:operation mappingResult:mappingResult ];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
[serverResponseDelegate serverResponseFailure:operation error:error];
}];
The EssenceRequest
- (id)initWithContextAndHandle:(NSString *)uniqueHandle essenceHandle:(NSString *)essenceUH;
{
self = [super init];
if (self != nil) {
_request = #{
#"__type" : #"SpecificEssenceLocationRequest:#Messaging.Media",
#"Action" : #"1",
#"ContextUH" : uniqueHandle,
#"EssenceUH" : essenceUH
};
}
return self;
}
+ (RKObjectMapping*)objectMapping
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:EssenceRequest.class];
[mapping addAttributeMappingsFromDictionary:#{
#"request": #"request"
}];
return mapping;
}
The "__type" item must be the first time in the JSON request body. Right now with it being in a dictionary it shows up later in the body when the dictionary is converted to a JSON string.
I know this is poor JSON handling on the server. They will fix it at some point and not require the __type any more but for now I need to send it as needed. I was able to do this in my Android code so I know the request will work once I have the NSString formatted.
Disclaimer: following answer is just my own opinion / suggestion.
Use +[RKMimeTypeSerialization unregisterClass:[RKNSJSONSerialization class]] to unregister default RestKit json serialization class. Then write your own class with "hacked" keys order. Register it through +[RKMimeTypeSerialization registerClass:[RKMYJSONSerialization class] forMINEType:RKMIMETypeJSON]
This way your won't change any API's - just "inject" your code into serialization/deserialization mechanism (and this is what you actually need).
The default implementation of RKNSJSONSerialization is quite simple:
+ (id)objectFromData:(NSData *)data error:(NSError **)error
{
return [NSJSONSerialization JSONObjectWithData:data options:0 error:error];
}
+ (NSData *)dataFromObject:(id)object error:(NSError **)error
{
return [NSJSONSerialization dataWithJSONObject:object options:0 error:error];
}
I think, you can go further yourself and write your own, based, of course on NSJSONSerialization or some another JSON serialization mechanism.
Taking Petro's answer a step further. This solution will maintain the functionality of all other requests.
After implementation you can wrap any JSON string in a SPRawJSON to send it as raw JSON for any request.
SPJSONSerialization.h
#import <RestKit/RestKit.h>
#interface SPRawJSON : NSObject
#property (nonatomic, readonly) NSString *json;
-(instancetype)initWithJSON:(NSString*)json;
+(RKObjectMapping*)mapping;
#end
#interface SPJSONSerialization : NSObject <RKSerialization>
#end
SPJSONSerialization.m
#import "SPJSONSerialization.h"
#implementation SPRawJSON
-(instancetype)initWithJSON:(NSString*)json
{
self = [super init];
if (self) {
_json = json;
}
return self;
}
+(RKObjectMapping*)mapping {
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[SPRawJSON class]];
[mapping addAttributeMappingsFromDictionary:#{ #"rawJSON": #"self" }];
return mapping;
}
#end
#implementation SPJSONSerialization
+ (id)objectFromData:(NSData *)data error:(NSError **)error {
return [RKNSJSONSerialization objectFromData:data error:error];
}
+ (NSData *)dataFromObject:(id)object error:(NSError **)error {
if ([object isKindOfClass:NSDictionary.class]) {
NSDictionary *dict = object;
id rawJSONObj = dict[#"rawJSON"];
if (rawJSONObj && [rawJSONObj isKindOfClass:SPRawJSON.class]) {
return [[(SPRawJSON*)rawJSONObj json] dataUsingEncoding:NSUTF8StringEncoding];
}
}
return [RKNSJSONSerialization dataFromObject:object error:error];
}
#end
Registering the mapping
RKObjectManager *objectManager = [RKObjectManager sharedManager];
// Make requests bodies be sent as JSON
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
// Add inverse mapping for the request
RKRequestDescriptor *descriptor = [RKRequestDescriptor requestDescriptorWithMapping:[SPRawJSON mapping].inverseMapping objectClass:[SPRawJSON class] rootKeyPath:nil method:RKRequestMethodPOST];
[objectManager addRequestDescriptor:descriptor];
Registering the JSON Serializer
// Replace standard JSON Serializer with our custom one that accepts raw json strings as well (SPRawJSON)
let currentJSONSerializer = RKMIMETypeSerialization.serializationClass(forMIMEType: RKMIMETypeJSON)
RKMIMETypeSerialization.unregisterClass(currentJSONSerializer)
RKMIMETypeSerialization.registerClass(SPJSONSerialization.self, forMIMEType: RKMIMETypeJSON)
Example code for sending request
NSString *myJSON = #"{\"exampleKey\": \"Example value\"}";
SPRawJSON *rawJSON = [[SPRawJSON alloc] initWithJSON:myJSON];
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[objectManager postObject:rawJSON path:#"foo/bar" parameters:nil success: ... failure: ...]
Notice that the mapping only maps POST requests, so if you want it to work for PUT, etc, you need to map that as well.
I'm stuck with a pagination issue.
When I go to /?PageSize=:perPage&Page=:page" I will get a json response like this:
Which I would like to map using the following paginator:
/* BBActivityPaginator */
RKObjectMapping *activityPaginationMapping = [RKObjectMapping mappingForClass:[BBActivityPaginator class]];
[activityPaginationMapping addAttributeMappingsFromDictionary:#{
#"Page" :#"currentPage",
#"PageSize" :#"perPage",
#"TotalResultCount" :#"objectCount"
}];
[activityPaginationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"PagedListItems" toKeyPath:#"activities" withMapping:activityMapping]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:activityPaginationMapping
pathPattern:#"/?PageSize=:perPage&Page=:page"
keyPath:#"Model.Activities"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
which is related to this mapping:
/* BBActivity */
RKObjectMapping *activityMapping = [RKObjectMapping mappingForClass:[BBActivity class]];
[activityMapping addAttributeMappingsFromDictionary:#{
#"Id" :#"identifier",
#"CreatedDateTime" :#"createdOn",
#"Description" :#"description",
#"CreatedDateTimeOrder" :#"order",
#"Type" :#"type",
#"DeletedActivityItem.Message" :#"deleted"
}];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"User" toKeyPath:#"user" withMapping:userMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"ObservationAdded.Observation" toKeyPath:#"observation" withMapping:observationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"SightingNoteAdded.SightingNote" toKeyPath:#"observationNote" withMapping:observationNoteMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"SightingNoteAdded.Sighting" toKeyPath:#"observationNoteObservation" withMapping:observationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"IdentificationAdded.Sighting" toKeyPath:#"identificationObservation" withMapping:observationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"IdentificationAdded.Identification" toKeyPath:#"identification" withMapping:identificationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"PostAdded.Post" toKeyPath:#"post" withMapping:postMapping]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:activityMapping
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
... For brevity I won't add all the mappings...
When I want to make my call to the paginator, I'm wiring up this:
-(void)setPaginatorForStream:(NSString*)streamName {
[BBLog Log:#"BBStreamController.setPaginatorForStream:"];
[BBLog Debug:#"streamName:" withMessage:streamName];
__weak typeof(self) weakSelf = self;
NSString *streamUrl = [NSString stringWithFormat:#"http://api.blahblah.org.au/%#?PageSize=:perPage&Page=:currentPage&X-Requested-With=XMLHttpRequest", streamName];
if (!self.paginator) {
paginationMapping = [RKObjectMapping mappingForClass:[BBActivityPaginator class]];
RKResponseDescriptor *activitiesResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[RKObjectMapping mappingForClass:[BBActivityPaginator class]]
pathPattern:nil
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
self.paginator = [[BBActivityPaginator alloc]initWithRequest:[NSURLRequest requestWithURL:[[NSURL alloc]initWithString:streamUrl]]
paginationMapping:paginationMapping
responseDescriptors:[[NSArray alloc]initWithObjects:activitiesResponseDescriptor, nil]
andDelegate:weakSelf];
}
self.paginator.perPage = 20;
[self.paginator setCompletionBlockWithSuccess:^(RKPaginator *paginator, NSArray *objects, NSUInteger page) {
[weakSelf.tableItems addObjectsFromArray:objects];
[weakSelf.tableView reloadData];
} failure:^(RKPaginator *paginator, NSError *error) {
NSLog(#"Failure: %#", error);
}];
Which is being activated in this method:
-(void)loadRequest {
[BBLog Log:#"BBStreamController.loadRequest"];
self.fetchBatch++;
[self.paginator loadPage:self.fetchBatch];
//[self.paginator setPaginatorLoading:YES];
self.loading = YES;
}
from the class initialisation:
-(BBStreamController*)initWithGroup:(NSString*)groupIdentifier
andDelegate:(id<BBStreamProtocol>)delegate {
[BBLog Log:#"BBStreamController.initWithGroup:andDelegate:"];
self = [self init];
if(self) {
_controller = delegate;
groupId = groupIdentifier;
[self setPaginatorForStream:groupIdentifier];
[self loadRequest];
}
[self loadView];
return self;
}
And getting nothing but pain...
Output:
E restkit:RKPaginator.m:207 Paginator didn't map info to compute page count. Assuming no pages.
2013-05-06 17:48:09.487 BowerBird[26570:5807] W restkit.object_mapping:RKMapperOperation.m:98 Adding mapping error: No mappable values found for any of the attributes or relationship mappings
Any one familiar with the finer details of RestKit 0.2.x's new pagination?
Your pathPattern and keyPath information needs to be set on the activitiesResponseDescriptor definition, not some paginator response descriptor that isn't actually used for anything.
The response descriptor is for the overall response and describes how to:
Tell that we have a match : pathPattern
Find the data to process : keyPath
Create the response objects : data mapping
The paginator mapping is additional to this and is only used to extract the page data from the response being processed.
Move the path pattern and key path to the response descriptor used for the request. The path pattern should also contain "PagedListItems" by the looks of it.
I am a beginner to RESTKIT and have only just recently tested it out on foursquare public api from ray's tutorial http://www.raywenderlich.com/13097/intro-to-restkit-tutorial.
Although i get the gist of it, there are some part which i do not understand, and would like pointer for it, so that i can consume my own web service.
- (void)viewDidLoad
{
[super viewDidLoad];
RKURL *baseURL = [RKURL URLWithBaseURLString:#"https://api.Foursquare.com/v2"];
RKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURL:baseURL];
objectManager.client.baseURL = baseURL;
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[Venue class]];
[venueMapping mapKeyPathsToAttributes:#"name", #"name", nil];
[objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"response.venues"];
[self sendRequest];
}
how do I change
[venueMapping mapKeyPathsToAttributes:#"name", #"name", nil];
[objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"response.venues"];
to accommodate my own webMethod? (my webMethod is shown below)
Currently, I upload my file to IIS for testing purpose, and am using the IP for web service. (I am constantly changing work area, so I designate it as myIPAddress for easier communication)
-- My service code (changed EDIT:now return JSON)
[WebMethod]
[ScriptMethod( ResponseFormat = ResponseFormat.Json)]
public void testTextJSON()
{
string text = "Testing for Json!";
List<string> arrayList = new List<string>();
arrayList.Add(text);
JavaScriptSerializer js = new JavaScriptSerializer();
string name = js.Serialize(arrayList);
Context.Response.Write(name);
}
return - ["Testing for Json!"]
EDIT- what I changed currently for viewDidLoad and sendRequest to test for my own service
- (void)viewDidLoad
{
[super viewDidLoad];
RKURL *baseURL = [RKURL URLWithBaseURLString:#"http://192.168.1.12"];
RKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURL:baseURL];
objectManager.client.baseURL = baseURL;
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[venue class]];
[venueMapping mapKeyPathsToAttributes:#"name", #"name", nil];
[self sendRequest];
}
and
- (void)sendRequest
{
RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKURL *URL = [RKURL URLWithBaseURL:[objectManager baseURL] resourcePath:#"/webService/webService1.asmx/testTextJSON"];
objectManager.acceptMIMEType = RKMIMETypeJSON;
objectManager.serializationMIMEType = RKMIMETypeJSON;
[[RKParserRegistry sharedRegistry] setParserClass:[RKJSONParserJSONKit class] forMIMEType:#"text/plain"];
[objectManager loadObjectsAtResourcePath:[NSString stringWithFormat:#"%#", [URL resourcePath]] delegate:self];
}
EDIT n+1 - here are some of my error message, maybe someone can tell me what went wrong?
2012-11-25 06:49:20.925 fourSquareAPI[352:12e03] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x8354430> valueForUndefinedKey:]: this class is not key value coding-compliant for the key venues.'
If I remove [objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"venues"];
I would get
2012-11-25 06:52:47.495 fourSquareAPI[368:11603] response code: 200
2012-11-25 06:52:47.499 fourSquareAPI[368:12e03] W restkit.object_mapping:RKObjectMapper.m:87 Adding mapping error: Could not find an object mapping for keyPath: ''
2012-11-25 06:52:47.499 fourSquareAPI[368:12e03] E restkit.network:RKObjectLoader.m:231 Encountered errors during mapping: Could not find an object mapping for keyPath: ''
2012-11-25 06:52:47.502 fourSquareAPI[368:11603] Error: Could not find an object mapping for keyPath: ''
Can someone please teach me what to do?? Any help would be greatly appreciated, I would really like to learn how to use RESTKIT to consume a webservice.
The key path response.venues is a path to the attribute with respect to the server response that is in JSON or XML format. In this case, the server returns "response" which has the key "venues". This is most likely a list of venues that you would apply the mapping to. You have to adapt that based on your server response.
If you want to handle a specific value before the mapping, use this function:
- (void)objectLoader:(RKObjectLoader *)loader willMapData:(inout __autoreleasing id *)mappableData; {
if([*mappableData valueForKey:#"result"] != nil){
[*mappableData removeObjectForKey:#"result"]; //This key won't be mapped now
}
}
I'm attempting to map the following object:
{"walletAccepted":false,"creditCardTypesAccepted":["visa","mastercard","discover","americanexpress"],"paypalAccepted":false}
To an object with identical attributes. However, the ResourcePath is dynamic, in that it's along the lines of /paymentmethods/zone/:internalZoneCode
Every attempt I've made at mapping it always ends up with "Encountered errors during mapping: Could not find an object mapping for keyPath: ''"
I think the issue is that there is no root key/key path. I've also attempted the following with no luck:
RKURL *rkUrl = [RKURL URLWithBaseURL:[RKClient sharedClient].baseURL resourcePath:[NSString stringWithFormat:#"/paymentoptions/zone/%#", zoneNumber]];
RKObjectLoader* loader = [[RKObjectLoader alloc] initWithURL:rkUrl mappingProvider:[RKObjectManager sharedManager].mappingProvider];
loader.method = RKRequestMethodGET;
loader.delegate = self;
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[PMZonePaymentMethods class]];
[loader send];
I found the way to make it happen is to configure the mapping, and add the mapping to the object manager without a key path or resourcek
So like this:
RKObjectMapping* zonePaymentMethodsMapping = [RKObjectMapping mappingForClass:[PMZonePaymentMethods class]];
[zonePaymentMethodsMapping mapKeyPath:#"walletAccepted" toAttribute:#"walletAvailable"];
[zonePaymentMethodsMapping mapKeyPath:#"creditCardTypesAccepted" toAttribute:#"cards"];
[zonePaymentMethodsMapping mapKeyPath:#"paypalAccepted" toAttribute:#"paypalAvailable"];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:zonePaymentMethodsMapping];
and use this to make the request:
RKURL *rkUrl = [RKURL URLWithBaseURL:[RKClient sharedClient].baseURL resourcePath:[NSString stringWithFormat:#"/paymentoptions/zone/%#", zoneNumber]];
RKObjectLoader* loader = [[RKObjectLoader alloc] initWithURL:rkUrl mappingProvider: [RKObjectManager sharedManager].mappingProvider];
loader.method = RKRequestMethodGET;
loader.delegate = self;
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[PMZonePaymentMethods class]];
[loader send];
I'm working a long time on this problem, but couldn't find a solution
that fits my needs.
The problem is, how could I load data and map them to the relationship
without loading the whole structure
Simple example:
We got some birds:
{
"birds":[{
"bird":{"id":"1","value":"LaLeLu"},
"bird":{"id":"2","value":"LeLeLa"},
...
}]
}
This could get mapped by something like this:
RKManagedObjectMapping *birdMapping = [RKManagedObjectMapping
mappingForClass:[Bird class]];
menuMapping.primaryKeyAttribute = #"identifier";
[menuMapping mapKeyPath:#"value" toAttribute:#"value"];
[menuMapping mapKeyPath:#"id" toAttribute:#"identifier"];
[[[RKObjectManager sharedManager] mappingProvider]
setMappingForKeyPath:"birds.bird"];
Works great by now.
Now every bird could have a lot of comments - but I don't wanne load
all these comments with the first request.
Comments should get loaded when users clicks the specific bird.
So I request:
NSString *resourcePath = [NSString stringWithFormat:#"/birds/%#/
comments", myBird.id]
[[RKObjectManager sharedManager]
loadObjectsAtResourcePath:resourcePath];
I could change the response that it fits the needs of RestKit - but
what are the needs?
{
"comments":[{
"comment"{"id":"1","value":"Comment1","bird_id":"1"}
}]
}
And now I don't have an idea how to map this response.
Mapping the comments without any relation to the birds is no problem:
RKManagedObjectMapping *commentMapping = [RKManagedObjectMapping
mappingForClass:[Comment class]];
menuMapping.primaryKeyAttribute = #"identifier";
[menuMapping mapKeyPath:#"value" toAttribute:#"value"];
[menuMapping mapKeyPath:#"id" toAttribute:#"identifier"];
[[[RKObjectManager sharedManager] mappingProvider]
setMappingForKeyPath:"comments.comment"];
Hope someone understands my problem and could help
For all who ware interested in the solution:
RestKit 0.10.0 fix the problem:
RKManagedObjectMapping *commentMapping = [RKManagedObjectMapping
mappingForClass:[Comment class]];
commentMapping.primaryKeyAttribute = #"identifier";
[commentMapping mapKeyPath:#"value" toAttribute:#"value"];
[commentMapping mapKeyPath:#"id" toAttribute:#"identifier"];
// Here starts the relevant part:
[commentMapping mapKeyPath:#"bird_id" to Attribute:#"bird_id"];
[commentMapping mapRelationship:#"bird" withMapping:birdMapping];
[commentMapping connectRelationship:#"bird" withObjectPropertyForPrimaryKeyAttribute:"bird_id"]
[[[RKObjectManager sharedManager] mappingProvider]
setMapping:commentMapping ForKeyPath:"comments.comment"];