RestKit nested relationships - objective-c

Working with RestKit, I'm trying to map the following JSON.
{
"userID": 1,
"collections": [
{
"collectionID": 120,
"friends": [
{
"friendID": 6,
"orders": [
{
"orderID": 1,
"name": "Small"
}
]
}
]
},
{
"collectionID": 123,
"friends": [
{
"friendID": 6,
"orders": [
{
"orderID": 2,
"name": "Medium"
}
]
}
]
}
]
}
I'm utilising RestKit and MagicalRecord - setup code, mapping and relationships below
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Model" ofType:#"momd"]];
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"db.sqlite"];
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:nil];
[managedObjectStore createManagedObjectContexts];
// Configure MagicalRecord to use RestKit's Core Data stack
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:managedObjectStore.persistentStoreCoordinator];
[NSManagedObjectContext MR_setRootSavingContext:managedObjectStore.persistentStoreManagedObjectContext];
[NSManagedObjectContext MR_setDefaultContext:managedObjectStore.mainQueueManagedObjectContext];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://127.0.0.1:3500"]];
objectManager.managedObjectStore = managedObjectStore;
RKEntityMapping *appMapping = [RKEntityMapping mappingForEntityForName:#"App" inManagedObjectStore:managedObjectStore];
appMapping.identificationAttributes = #[ #"userID" ];
RKEntityMapping *collectionMapping = [RKEntityMapping mappingForEntityForName:#"Collection" inManagedObjectStore:managedObjectStore];
collectionMapping.identificationAttributes = #[ #"collectionID" ];
[collectionMapping addAttributeMappingsFromArray:#[#"collectionID"]];
RKEntityMapping *friendMapping = [RKEntityMapping mappingForEntityForName:#"Friend" inManagedObjectStore:managedObjectStore];
friendMapping.identificationAttributes = #[ #"friendID" ];
[friendMapping addAttributeMappingsFromArray:#[#"friendID"]];
RKEntityMapping *orderMapping = [RKEntityMapping mappingForEntityForName:#"Order" inManagedObjectStore:managedObjectStore];
orderMapping.identificationAttributes = #[ #"orderID" ];
[orderMapping addAttributeMappingsFromArray:#[#"orderID", #"name"]];
RKRelationshipMapping *appCollectionRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:#"collections" toKeyPath:#"collections" withMapping:collectionMapping];
[appMapping addPropertyMapping:appCollectionRelationship];
RKRelationshipMapping *collectionFriendRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:#"friends" toKeyPath:#"friends" withMapping:friendMapping];
[collectionMapping addPropertyMapping:collectionFriendRelationship];
RKRelationshipMapping *friendsOrderRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:#"orders" toKeyPath:#"orders" withMapping:orderMapping];
[friendsOrderRelationship setAssignmentPolicy:RKUnionAssignmentPolicy];
[friendMapping addPropertyMapping:friendsOrderRelationship];
Then, querying the /test route on my API (which outputs the JSON block above) with the below console out...
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:appMapping method:RKRequestMethodAny pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
[objectManager getObjectsAtPath:#"/test" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
App *app = [mappingResult firstObject];
for (Collection *collection in app.collections) {
NSLog(#"collectionID = %#", collection.collectionID);
for (Friend *friend in collection.friends) {
NSLog(#"friendID = %#", friend.friendID);
for (Order *order in friend.orders) {
NSLog(#"Name = %#", order.name);
}
}
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {}];
Provides the following output - notice both Name's are Medium, not one Small and the other Medium.
collectionID = 120
friendID = 6
Name = Medium
Name = Small
collectionID = 123
friendID = 6
Name = Medium
Name = Small
Why would this be happening? I'm going to guess that it's because both friend ID's are the same, even though they're under different collections...

This is because you have identificationAttributes = #[ #"friendID" ]; and the default relationship connection rule for relationships rather than replacement. So, the second mapping finds the friend created by the first mapping and replaces the order relationship contents.
You can't realistically change the unique identifier based on the JSON you show, but you might want to examine that (in relation to your object graph requirements).
Also / alternatively, see this answer for details of how to change the relationship assignment policy so that the original relationship contents aren't lost.

Related

RestKit - Data Mapping

I'm new in RestKit.
Can someone help me with mapping. I have JSON and I need to read and save data from JSON. How I can do it?
{"data": {
"viewer": {
"themes": {
"edges": [
{
"node": {
"id": "61c39",
"name": "ff"
}
}
{
"node": {
"id": "dd95af4b-"",
"name": "growth",
}
}
]
}
}
}
}
Here is a part of my code for mapping:
// Defines mapping for DefaultThemes
RKEntityMapping *mappingDefaultTheme = [RKEntityMapping mappingForEntityForName:#"DefaultThemeData"
inManagedObjectStore:self.managedObjectStore];
mappingDefaultTheme.identificationAttributes = #[#"themeId"];
[mappingDefaultTheme addAttributeMappingsFromDictionary:#{ #"node.id" : #"themeId", #"node.name" : #"name"}];
RKResponseDescriptor *themeDefaultListResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mappingDefaultTheme
method:RKRequestMethodGET
pathPattern:#"graphql"
keyPath:#"edges.node"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
I have error when I running the app:
Error: No mappable object representations were found at the key paths searched.
So I need to know, how I should do data mapping?
The key path data.viwer.themes.edges.node is too deep because you're trying to drill down into an array and that isn't possible. The longest key path you can have is data.viwer.themes.edges and then the mapping will process each of the objects containing 'nodes'.
Here is my solution:
RKObjectMapping* chartDataDetailMapping = [RKObjectMapping mappingForClass:[MADefaultThemeData class]];
[chartDataDetailMapping addAttributeMappingsFromDictionary:#{#"id": #"themeId",
#"name": #"name"}];
RKObjectMapping* chartDataMapping = [RKObjectMapping mappingForClass:[MANode class]];
[chartDataMapping addAttributeMappingsFromDictionary:#{#"edges":#"edges"}];
RKObjectMapping* alertInstanceMapping = [RKObjectMapping mappingForClass:[MADefaultThemeList class]];
[alertInstanceMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"edges"
toKeyPath:#"edges"
withMapping:chartDataMapping]];
[chartDataMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"node"
toKeyPath:#"themeData"
withMapping:chartDataDetailMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:alertInstanceMapping
method:RKRequestMethodPOST
pathPattern:#"graphql"
keyPath:#"data.viewer.themes"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self addResponseDescriptor:responseDescriptor];

Restkit 0.20 keypath error 1001

I used Restkit 0.20 (first time) for posting objects to a Rails server via JSON, and I 've been stuck for the last days with an annoying mapping error. Got 2 objects Picture and Item.
Picture
{
"base64" : null,
"id" : 1,
"created_at" : "2013-08-28T15:08:49Z",
"user_nickname" : "user_name",
"tag" : "ALPHA",
"nbitems" : null,
"path" : {
"url" : "https://www.mywebsite/pictures/1/default.jpg"
},
"updated_at" : "2013-08-28T15:08:49Z",
"user_id" : 1
}
Item
{
"base64" : null,
"picture_id" : 1,
"id" : 1,
"created_at" : "2013-08-28T15:10:54Z",
"itemurl" : {
"url" : "https://www.mywebsite/pictures/1/item1.png"
},
"nickname" : "",
"user_id" : 1,
"updated_at" : "2013-08-28T15:10:54Z"
}
Here's my mapping in the AppDelegate.m
//Mapping users
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([User class]) inManagedObjectStore:manager.managedObjectStore];
[userMapping addAttributeMappingsFromDictionary:#{
#"id" : #"user_id",
#"email" : #"email",
#"nickname" : #"nickname",
#"created_at" : #"createdAt"
}];
userMapping.identificationAttributes = #[ #"user_id" ];
//Mapping paths
RKEntityMapping *pathMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([Path class]) inManagedObjectStore:manager.managedObjectStore];
[pathMapping addAttributeMappingsFromDictionary:#{
#"url" : #"url"
}];
//Mapping itemURL
RKEntityMapping *itemURLMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([ItemURL class]) inManagedObjectStore:manager.managedObjectStore];
[pathMapping addAttributeMappingsFromDictionary:#{
#"url" : #"url"
}];
//Mapping pictures
RKEntityMapping *pictureMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([Picture class]) inManagedObjectStore:managedObjectStore];
[pictureMapping addAttributeMappingsFromDictionary:#{
#"id" : #"picture_id",
#"user_id" : #"user_id",
#"user_nickname" : #"user_nickname",
#"nbitems" : #"nbitems",
#"tag" : #"tag",
#"base64" : #"base64",
#"created_at" : #"createdAt"
}];
pictureMapping.identificationAttributes = #[ #"picture_id"];
[pictureMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"path" toKeyPath:#"path" withMapping:pathMapping]];
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:pictureMapping
pathPattern:#"pictures"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]
]];
RKObjectMapping *pictureRequestMapping = [RKObjectMapping requestMapping];
[pictureRequestMapping addAttributeMappingsFromArray:#[#"user_id", #"user_nickname", #"tag", #"base64", #"path"]];
RKRequestDescriptor *pictureRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:pictureRequestMapping objectClass:[Picture class] rootKeyPath:#"picture" method:RKRequestMethodAny];
[manager addRequestDescriptor:pictureRequestDescriptor];
//Mapping items
RKEntityMapping *itemMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([Item class]) inManagedObjectStore:managedObjectStore];
itemMapping.identificationAttributes = #[ #"item_id"];
[itemMapping addAttributeMappingsFromDictionary:#{
#"id" : #"item_id",
#"picture_id" : #"picture_id",
#"user_id" : #"user_id",
#"nickname" : #"nickname",
#"base64" : #"base64",
#"created_at" : #"createdAt"
}];
RKRelationshipMapping *itemURLRelation = [RKRelationshipMapping relationshipMappingFromKeyPath:#"itemurl" toKeyPath:#"itemurl" withMapping:itemURLMapping];
[itemMapping addPropertyMapping:itemURLRelation];
RKResponseDescriptor *itemDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:itemMapping
pathPattern:#"items"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[manager addResponseDescriptor:itemDescriptor];
RKObjectMapping *itemRequestMapping = [RKObjectMapping requestMapping];
[itemRequestMapping addAttributeMappingsFromArray:#[#"picture_id", #"user_id", #"nickname", #"base64", #"itemurl"]];
RKRequestDescriptor *itemRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:itemRequestMapping objectClass:[Item class] rootKeyPath:#"item" method:RKRequestMethodAny];
[manager addRequestDescriptor:itemRequestDescriptor];
I post a Picture instance in a ViewController and it works perfectly
- (void)uploadPicture:(UIImage *)chosenImage{
NSManagedObjectContext *moc = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
UIImage *resizedImage = [self scaleImage:chosenImage maxWidth:640 maxHeight:960];
NSString *imageEncoded = [self image2jpg2String:resizedImage];
Picture *picture = [Picture insertInManagedObjectContext:moc];
picture.user_id = userId;
picture.user_nickname = #"user_nickname";
picture.tag = #"ALPHA";
picture.base64 = imageEncoded;
[[RKObjectManager sharedManager] postObject:picture path:#"pictures" parameters:nil success:nil failure:^(RKObjectRequestOperation *operation, NSError *error) { NSLog(#"Error: %#",error);}];
}
I try to do the same with an Item instance in another controller
- (void)uploadItem{
NSManagedObjectContext *mocFVC = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSNumber *userId = [NSNumber numberWithInt:1];
UIImage *itemImage = self.itemImage.image;
UIImage *resizedItem = [self scaleImage:itemImage maxWidth:640 maxHeight:960];
NSString *itemEncoded = [self image2png2String:resizedItem];
Item *item = [Item insertInManagedObjectContext:mocFVC];
item.user_id = userId;
item.nickname = #"user_nickname";
item.picture_id = pictureToDisplay.picture_id;
item.base64 = itemEncoded;
[[RKObjectManager sharedManager] postObject:item path:#"items" parameters:nil success:nil failure:^(RKObjectRequestOperation *operation, NSError *error) { NSLog(#"Error: %#",error);}];
}
But unless I comment the item.base64, xcode always returns the following error
2013-08-28 22:51:41.116 MYPROJECT[4588:1603] E restkit.network:RKObjectRequestOperation.m:243 POST 'http://www.mywebsite/items' (422 Unprocessable Entity / 0 objects) [request=0.6791s mapping=0.0000s total=0.7111s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No mappable object representations were found at the key paths searched."
UserInfo=0x1e8af330 {DetailedErrors=(), NSLocalizedFailureReason=The mapping operation was unable to find any nested object representations at the key paths searched: error
The representation inputted to the mapper was found to contain nested object representations at the following key paths: itemurl
This likely indicates that you have misconfigured the key paths for your mappings., NSLocalizedDescription=No mappable object representations were found at the key paths searched., keyPath=null}
2013-08-28 22:51:41.119 MYPROJECT[4588:907] Error: Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No mappable object representations were found at the key paths searched." UserInfo=0x1e8af330 {DetailedErrors=(), NSLocalizedFailureReason=The mapping operation was unable to find any nested object representations at the key paths searched: error
The representation inputted to the mapper was found to contain nested object representations at the following key paths: itemurl
This likely indicates that you have misconfigured the key paths for your mappings., NSLocalizedDescription=No mappable object representations were found at the key paths searched., keyPath=null}
I checked a lot of post on stackoverflow and read the restkit doc before posting, but don't understand why it doesn't work for my item since it's code and picture model are very closed to the one I use for my Item class. Am I missing something ? Any idea ? :)
#Wain the logs didn't show anything
Turns out the Restkit part was correct. The error was happening in my base64/upload function in the model.
def base64=(data)
# decode data and create stream on them
filename = "#{self.user_id}.png"
io = CarrierStringIO.new(filename, Base64.decode64(data))
# this will do the thing (photo is mounted carrierwave uploader)
self.path = io
end
The filename line works in a webbrowser but not using JSON. #weird
Had to change the path of the file to keep the user_id as data

RestKit - Sending POST & Receive Response mapped

So, i m using RestKit version 0.20 and i m successfully sending a POST request as JSON. My Server backend (Java REST WS (Jersey)) is mapping everything right, aswell Restkit.
My Problem is now that i m sending a different Object back as that I have Post. I have following mapping setup in RestKit:
- (void)createUserAccount:(DeviceDTO *)devDTO :(UserDTO *)userDTO block:(void (^)(id))block{
id errorCode __block;
// Configure a request mapping for our Article class. We want to send back title, body, and publicationDate
RKObjectMapping* deviceRequestMapping = [RKObjectMapping requestMapping];
[deviceRequestMapping addAttributeMappingsFromArray:#[ #"model", #"name", #"systemName", #"systemVersion", #"devToken" ]];
RKObjectMapping* msRequestMapping = [RKObjectMapping requestMapping];
[msRequestMapping addAttributeMappingsFromArray:#[ #"validSince", #"validTill" ]];
RKObjectMapping* countryRequestMapping = [RKObjectMapping requestMapping];
[countryRequestMapping addAttributeMappingsFromArray:#[ #"idNumberDTO", #"iso2DTO", #"short_nameDTO", #"calling_codeDTO" ]];
RKObjectMapping* contactsRequestMapping = [RKObjectMapping requestMapping];
[contactsRequestMapping addAttributeMappingsFromArray:#[ #"fullName", #"phoneNumber"]];
RKObjectMapping* userRequestMapping = [RKObjectMapping requestMapping];
[userRequestMapping addAttributeMappingsFromArray:#[ #"displayName", #"phoneNumber", #"status", #"userID", #"realName" ]];
[userRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"deviceInfo" toKeyPath:#"device" withMapping:deviceRequestMapping]];
[userRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"memberShipDetails" toKeyPath:#"memberShip" withMapping:msRequestMapping]];
[userRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"country" toKeyPath:#"country" withMapping:countryRequestMapping]];
[userRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"contacts" toKeyPath:#"contacts" withMapping:contactsRequestMapping]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:userRequestMapping objectClass:[UserDTO class] rootKeyPath:nil];
//Create Objects
UserDTO *user = [[UserDTO alloc]init];
..........
DeviceDTO *device = [[DeviceDTO alloc]init];
..........
user.deviceInfo = device;
MemberShipDTO *ms = [[MemberShipDTO alloc]init];
.......
user.memberShipDetails = ms;
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[ErrorMapping class]];
[errorMapping addAttributeMappingsFromArray:#[ #"errorCode", #"errorMessage" ]];
RKObjectMapping* deviceRespMapping = [RKObjectMapping mappingForClass:[DeviceDTO class]];
[deviceRespMapping addAttributeMappingsFromArray:#[ #"model", #"name", #"systemName", #"systemVersion", #"devToken" ]];
RKObjectMapping* msRespMapping = [RKObjectMapping mappingForClass:[MemberShipDTO class]];
[msRespMapping addAttributeMappingsFromArray:#[ #"validSince", #"validTill" ]];
RKObjectMapping* contactsRespMapping = [RKObjectMapping mappingForClass:[ContactDTO class]];
[contactsRespMapping addAttributeMappingsFromArray:#[ #"fullName", #"phoneNumber"]];
RKObjectMapping* userRespMapping = [RKObjectMapping mappingForClass:[UserDTO class]];
[userRespMapping addAttributeMappingsFromArray:#[ #"displayName", #"phoneNumber", #"status", #"userID", #"realName" ]];
[userRespMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"deviceInfo" toKeyPath:#"device" withMapping:deviceRespMapping]];
[userRespMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"memberShipDetails" toKeyPath:#"memberShip" withMapping:msRespMapping]];
[userRespMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"country" toKeyPath:#"country" withMapping:countryRequestMapping]];
[userRespMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"contacts" toKeyPath:#"contacts" withMapping:contactsRespMapping]];
[errorMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"user" toKeyPath:#"user" withMapping:userRespMapping]];
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"application/json"];
[[RKObjectManager sharedManager] setRequestSerializationMIMEType:RKMIMETypeJSON];
[[RKObjectManager sharedManager] setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[[RKObjectManager sharedManager] addRequestDescriptor:requestDescriptor];
[[RKObjectManager sharedManager] addResponseDescriptor:errorDescriptor];
[[RKObjectManager sharedManager] postObject:user path:#"user/integrate" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){
NSArray* statuses = [mappingResult array];
NSLog(#"Loaded statuses: %#", statuses);
errorCode = [statuses objectAtIndex:0];
NSLog(#"errorCode == %#", errorCode);
block(errorCode);
RKLogInfo(#"Load collection of Articles: %#", mappingResult.array);
}failure:^(RKObjectRequestOperation *operation, NSError *error) {
block(nil);
RKLogError(#"Operation failed with error: %#", error);
}];
}
My JSON on my request is fine & the response is also fine:
{
"errorMessage": null,
"errorCode": 190,
"user": {
"displayName": "Saif",
"phoneNumber": "+xxx",
"userID": "xxx",
"country": {
"idNumberDTO": 83,
"short_nameDTO": "Germany",
"calling_codeDTO": "+49",
"iso2DTO": "DE"
},
"device": {
"devToken": "xxx",
"model": "iPhone",
"name": "Saifs iPhone",
"systemName": "iPhone OS",
"systemVersion": "6.1.4",
"id": null
},
"memberShip": {
"validSince": 1376047810000,
"validTill": 1407583810000,
"id": null
},
"contacts": [
{
"fullName": "xxx",
"phoneNumber": "xxx"
},
{
"fullName": "xxx",
"phoneNumber": "xxx"
},
....,
....,
....
],
"id": null
}
}
On my RK methode i get on this line:
errorCode = [statuses objectAtIndex:0];
this Error:
2013-08-09 13:30:12.246 xxx![19310:440f] W restkit.object_mapping:RKMapperOperation.m:99 Adding mapping error: Expected an object mapping for class of type 'UserDTO', provider returned one for 'ErrorMapping'
2013-08-09 13:30:12.247 xxx![19310:440f] I restkit.network:RKObjectRequestOperation.m:250 POST 'http://192.168.2.115:8080/WAZZUUPWS/rest/service/user/integrate' (200 OK / 0 objects) [request=1.3406s mapping=0.0047s total=1.3461s]
2013-08-09 13:30:12.249 xxx![19310:907] Loaded statuses: (
)
2013-08-09 13:30:12.250 xxx![19310:907] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
It looks like the response couldnt be mapped and thats why the Resultarray is empty. Any idea how i can map a complex object from a response?
From the Restkit documentation:
By default when postObject or putObject are used, RestKit is automatically going to try to map the JSON result into the sourceObject that was posted.
In your code, you are posting an UserDTO instance and receiving a JSON response that should map to ErrorMapping. Hence, you are getting RKMappingErrorTypeMismatch from Restkit.
As explained in the documentation, instead of using post, you can create an RKRequestOperation yourself and nil the target object to override the defaults.
RKObjectRequestOperation *operation = [[RKObjectManager sharedManager] appropriateObjectRequestOperationWithObject:user
method:RKRequestMethodPOST
path:#"user/integrate"
parameters:nil];
operation.targetObject = nil;
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
DLog(#"integrated user: %#", mappingResult.firstObject);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
DLog(#"error integrating user: %#", user);
}];
However, if you can modify the backend, I'd prefer to change the returned JSON. Instead of including the error codes in the JSON response, use relevant (HTTP) response status codes and include only domain model specific entities in your responses.

RestKit - Entity Mapping Property transformation

I need to map a web service with object properties all in caps against coredata object properties all in lowercase:
# JSON
{
'ID': 'value',
'TITLE': 'value',
'BODY': 'value',
}
# CoreData Entity
{
'id': 'value',
'title': 'value',
'body': 'value',
}
I mapped the fields by doing:
RKEntityMapping *entMap = [RKEntityMapping mappingForEntityForName:entName
inManagedObjectStore:managedObjectStore];
NSEntityDescription *entity = [[managedObjectModel entitiesByName]
objectForKey:entName];
[entMap addAttributeMappingsFromArray:[[[RKPropertyInspector sharedInspector]
propertyInspectionForEntity:entity] allKeys]];
I saw this convenient setDefaultSourceToDestinationKeyTransformationBlock fonction in RKObjectMapping allowing to define custom transformation on object properties. This is not available on RKEntityMapping.
How can I do property transformations with RKEntityMapping without defining fields manually ?
Here is my current work-around:
- (NSDictionary *)dictToArray:(NSArray *)array
withTranformation:(NSString *(^)(NSString *))theBlock
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSString *key in array) {
dict[theBlock(key)] = key;
}
return dict;
}
NSArray *fields = [[[RKPropertyInspector sharedInspector]
propertyInspectionForEntity:entity] allKeys];
NSDictionary *mapDict = [self dictToArray:fields
withTranformation:^NSString *(NSString *str) {
return [str uppercaseString];
}];
[entMap addAttributeMappingsFromDictionary:mapDict];

How to use Restkit to POST JSON and map response

I'm using RestKit for the first time, and its feature-set looks great. I've read the document multiple times now and I'm struggling to find a way to POST JSON params to a feed and map the JSON response. From searching on stackoverflow I found a way to send the JSON params via a GET, but my server only takes POST.
Here is the code I have so far:
RKObjectMapping *issueMapping = [RKObjectMapping mappingForClass:[CDIssue class]];
[objectMapping mapKeyPath:#"issue_id" toAttribute:#"issueId"];
[objectMapping mapKeyPath:#"title" toAttribute:#"issueTitle"];
[objectMapping mapKeyPath:#"description" toAttribute:#"issueDescription"];
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:#"http://restkit.org"];
RKManagedObjectStore* objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:#"News.sqlite"];
objectManager.objectStore = objectStore;
NSDictionary params = [NSDictionary dictionaryWithObjectsAndKeys: #"myUsername", #"username", #"myPassword", #"password", nil];
NSURL *someURL = [objectManager.client URLForResourcePath:#"/feed/getIssues.json" queryParams:params];
[manager loadObjectsAtResourcePath:[someURL absoluteString] objectMapping:objectMapping delegate:self]
From the another stackoverflow thread (http://stackoverflow.com/questions/9102262/do-a-simple-json-post-using-restkit), I know how to do a simple POST request with the following code:
RKClient *myClient = [RKClient sharedClient];
NSMutableDictionary *rpcData = [[NSMutableDictionary alloc] init ];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
//User and password params
[params setObject:password forKey:#"password"];
[params setObject:username forKey:#"email"];
//The server ask me for this format, so I set it here:
[rpcData setObject:#"2.0" forKey:#"jsonrpc"];
[rpcData setObject:#"authenticate" forKey:#"method"];
[rpcData setObject:#"" forKey:#"id"];
[rpcData setObject:params forKey:#"params"];
//Parsing rpcData to JSON!
id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:RKMIMETypeJSON];
NSError *error = nil;
NSString *json = [parser stringFromObject:rpcData error:&error];
//If no error we send the post, voila!
if (!error){
[[myClient post:#"/" params:[RKRequestSerialization serializationWithData:[json dataUsingEncoding:NSUTF8StringEncoding] MIMEType:RKMIMETypeJSON] delegate:self] send];
}
I was hoping someone would help me marry these two code snippets into a workable solution.
To post an object what I do is associate a path to an object. Then use the method postObject from RKObjectManager.
I asume that you have already configured RestKit so you have the base path set and defined the object mapping for your CDIssue as you have in the code that you already have. With that in mind try this code:
//We tell RestKit to asociate a path with our CDIssue class
RKObjectRouter *router = [[RKObjectRouter alloc] init];
[router routeClass:[CDIssue class] toResourcePath:#"/path/to/my/cdissue/" forMethod:RKRequestMethodPOST];
[RKObjectManager sharedManager].router = router;
//We get the mapping for the object that you want, in this case CDIssue assuming you already set that in another place
RKObjectMapping *mapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[CDIssue class]];
//Post the object using the ObjectMapping with blocks
[[RKObjectManager sharedManager] postObject:myEntity usingBlock:^(RKObjectLoader *loader) {
loader.objectMapping = mapping;
loader.delegate = self;
loader.onDidLoadObject = ^(id object) {
NSLog(#"Got the object mapped");
//Be Happy and do some stuff here
};
loader.onDidFailWithError = ^(NSError * error){
NSLog(#"Error on request");
};
loader.onDidFailLoadWithError = ^(NSError * error){
NSLog(#"Error on load");
};
loader.onDidLoadResponse = ^(RKResponse *response) {
NSLog(#"Response did arrive");
if([response statusCode]>299){
//This is useful when you get an error. You can check what did the server returned
id parsedResponse = [KFHelper JSONObjectWithData:[response body]];
NSLog(#"%#",parsedResponse);
}
};
}];