Restkit 0.20 keypath error 1001 - ruby-on-rails-3

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

Related

RestKit nested relationships

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.

RestKit - Relashionship Mapping with CoreData - Response Error

I have a Guest entity and inside guest is a City entity. I've setup a relashionship in CoreData and RestKit using the RKRelationshipMapping.
I've mapped a single entity correctly, but when I use RKRelationshipMapping to add a City Entity it seems to generate a this error while calling the following method with the #"cityId" property:
-[RKPropertyInspector classForPropertyNamed:ofEntity:]: unrecognized selector sent to instance
My mapping code:
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:self.serverBaseAddress]];
objectManager.managedObjectStore = [RKManagedObjectStore defaultStore];
[RKObjectManager setSharedManager:objectManager];
RKEntityMapping *cityEMapping = [RKEntityMapping mappingForEntityForName:self.cityEntityName inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[cityEMapping addAttributeMappingsFromDictionary:#{
#"id" : #"cityId",
#"name" : #"name"
}];
RKEntityMapping *guestEMapping = [RKEntityMapping mappingForEntityForName:self.entityName inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[guestEMapping addAttributeMappingsFromDictionary:#{
#"name" : #"name",
#"photo" : #"photo",
#"comment" : #"comment",
#"id" : #"guestId",
#"date" : #"date"
}];
[guestEMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"city"
toKeyPath:#"city"
withMapping:cityEMapping]];
guestEMapping.identificationAttributes = #[ #"guestId" ];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:guestEMapping method:RKRequestMethodAny pathPattern:self.postGetAddress keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
And where the app stops: (propertyClass receiving 0x00 in propertyClass = [[RKPropertyInspector sharedInspector]...)
- (Class)classForKeyPath:(NSString *)keyPath
{
NSArray *components = [keyPath componentsSeparatedByString:#"."];
Class propertyClass = self.objectClass;
for (NSString *property in components) {
propertyClass = [[RKPropertyInspector sharedInspector] classForPropertyNamed:property ofClass:propertyClass isPrimitive:nil];
if (! propertyClass) propertyClass = [[RKPropertyInspector sharedInspector] classForPropertyNamed:property ofEntity:self.entity];
if (! propertyClass) break;
}
return propertyClass;
}
And here is the json:
[{"id":"3","name":"Convidado3","city":{"id":"2","name":"São Paulo"},"date":"Data2","comment":"comentario3","photo":":P"},
{"id":"2","name":"Convidado2","city":{"id":"1","name":"Rio de Janeiro"},"date":"Data2","comment":"comentario2","photo":":|"},
{"id":"1","name":"Convidado1","city":{"id":"1","name":"Rio de Janeiro"},"date":"Data1","comment":"comentario1","photo":":)"},
{"id":"5","name":"Convidado5","city":{"id":"4","name":"Vitória"},"date":"Data4","comment":"comentario5","photo":":K"},
{"id":"4","name":"Convidado4","city":{"id":"3","name":"Belo Horizonte"},"date":"Data3","comment":"comentario4","photo":";)"}]
The entity Guest has a relationship called city which is connected to the City Entity through a relationship called guests in the following manner:
Guest.....City
city<--->>guests
If I simply remove the relationship it works ok, without city sub object...
Any help or suggestions appreciated...
I've solved the problem:
After going through all the steps in debug, I changed the following line
From:
RKEntityMapping *cityEMapping = [RKEntityMapping mappingForEntityForName:self.cityEntityName inManagedObjectStore:[RKManagedObjectStore defaultStore]];
To:
RKEntityMapping *cityEMapping = [RKEntityMapping mappingForEntityForName:#"City" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
And it worked!
I don't understand the problem, as I checked the value of self.cityEntityName during debug and it is #"City", so technically it should work. As for the line:
RKEntityMapping *guestEMapping = [RKEntityMapping mappingForEntityForName:self.entityName inManagedObjectStore:[RKManagedObjectStore defaultStore]];
works just fine... without any changes, I don't know what to say. Thanks for the suggestions.

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.

Non-deprecated means of setting object mapping

I have a Ruby On Rails local server running which serves me the following JSON:
[ { "content" : "Here is content",
"created_at" : "2012-10-07T01:06:51Z",
"id" : 4,
"name" : "Joe",
"title" : "Title 1",
"updated_at" : "2012-10-07T01:06:51Z"
},
{ "content" : "Here is content",
"created_at" : "2012-10-07T01:07:09Z",
"id" : 5,
"name" : "John",
"title" : "Title2",
"updated_at" : "2012-10-07T01:07:09Z"
}
]
I am using RestKit to parse this into Post model objects like so:
- (void)loadObjects
{
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[Post class]];
[objectMapping mapKeyPath:#"id" toAttribute:#"postId"];
[objectMapping mapKeyPath:#"created_at" toAttribute:#"createdAt"];
[objectMapping mapKeyPath:#"content" toAttribute:#"content"];
[objectMapping mapKeyPath:#"title" toAttribute:#"title"];
[objectMapping mapKeyPath:#"name" toAttribute:#"name"];
[objectMapping mapKeyPath:#"updated_at" toAttribute:#"updatedAt"];
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURLString:#"http://localhost:3000/"];
[manager loadObjectsAtResourcePath:#"/posts" objectMapping:objectMapping delegate:self];
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
NSLog(#"%#", objects);
}
This works fine, however the loadObjectsAtResourcePath:objectMapping:delegate: is deprecated. I assume I should therefore be using loadObjectsAtResourcePath:delegate:, but I cannot figure out a way to set the objectMapping. Any thoughts?
Update
I am now setting the delegate like so:
[manager loadObjectsAtResourcePath:#"/posts" usingBlock:^(RKObjectLoader *loader) {
loader.objectMapping = objectMapping;
loader.delegate = self;
}];
The restkit documentation says that if you use loadObjectsAtResourcePath:usingBlock: you can set the objectMapping from inside the a block.
- (void)loadObjectUsingBlockExample {
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/monkeys.json" usingBlock:^(RKObjectLoader *loader) {
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Monkey class]];
}];
}

RestKit Image Upload

I am using RestKit to drive interactions with my web server. I am trying to use routing to POST an Event object to the server with an image attached to it. The code is as follows:
RKObjectManager *manager = [RKObjectManager sharedManager];
RKObjectMapping *map = [self eventMapping];
manager.serializationMIMEType = RKMIMETypeFormURLEncoded;
map.rootKeyPath = #"event";
[manager.mappingProvider setSerializationMapping:map forClass:[Event class]];
[manager.router routeClass:[Event class] toResourcePath:#"/v1/events.json" forMethod:RKRequestMethodPOST];
[manager postObject:event delegate:self block:^(RKObjectLoader *loader){
RKObjectMapping *serMap = [[[RKObjectManager sharedManager] mappingProvider] serializationMappingForClass:[Event class]];
NSError *error = nil;
NSDictionary *d = [[RKObjectSerializer serializerWithObject:event mapping:serMap] serializedObject:&error];
RKParams *p = [RKParams paramsWithDictionary:d];
[p setData:[event imageData] MIMEType:#"image/jpeg" forParam:#"image"];
loader.params = p;
}];
If I create an instance of RKParams using the serialized Event object, then add the image data and assign it as the RKObjectLoader's params property, all the properties become one massive serialized string. There must be a way to upload an image without the massive string serialization.
I have also tried having an NSData property that is mapped to some attribute, converting a UIImage into NSData along the way, but RestKit complains that it can't be mapped. Has anyone done this before?
I did something very similar and it worked out just fine. I realize your question is about why RKObjectSerializer isn't working the way you expect, but maybe it is something else with your setup. I'm posting my code to give a clean example of something that does work. That said, after reading the RKObjectSerializer documentation, I don't see why you couldn't initialize your RKParams that way instead of setting them directly as I do in my example.
Router setup:
RKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURL:kApiUrlBase];
[objectManager.router routeClass:[PAPetPhoto class] toResourcePath:#"/pet/uploadPhoto" forMethod:RKRequestMethodPOST];
Mapping setup:
RKObjectMapping *papetPhotoMapping = [RKObjectMapping mappingForClass:[PAPetPhoto class]];
[papetPhotoMapping mapKeyPath:#"id" toAttribute:#"identifier"];
[papetPhotoMapping mapAttributes:#"accountId", #"petId", #"photoId", #"filename", #"contentType", nil];
[objectManager.mappingProvider addObjectMapping:papetPhotoMapping];
[objectManager.mappingProvider setSerializationMapping:[papetPhotoMapping inverseMapping] forClass:[PAPetPhoto class]];
[objectManager.mappingProvider setMapping:papetPhotoMapping forKeyPath:#"petPhoto"];
The post: (notice since I built up all my params in the block my object is just a dummy instance to trigger the proper routing and mapper).
PAPetPhoto *photo = [[PAPetPhoto alloc] init];
[[RKObjectManager sharedManager] postObject:photo delegate:self block:^(RKObjectLoader *loader){
RKParams* params = [RKParams params];
[params setValue:pet.accountId forParam:#"accountId"];
[params setValue:pet.identifier forParam:#"petId"];
[params setValue:_photoId forParam:#"photoId"];
[params setValue:_isThumb ? #"THUMB" : #"FULL" forParam:#"photoSize"];
[params setData:data MIMEType:#"image/png" forParam:#"image"];
loader.params = params;
}];
Server endpoint (Java, Spring MVC)
#RequestMapping(value = "/uploadPhoto", method = RequestMethod.POST)
#ResponseBody
public Map<String, Object> handleFormUpload(#RequestParam("accountId") String accountId,
#RequestParam("petId") String petId,
#RequestParam("photoId") String photoId,
#RequestParam("photoSize") PhotoSizeEnum photoSize,
#RequestParam("image") Part image) throws IOException {
if (log.isTraceEnabled())
log.trace("uploadPhoto. accountId=" + accountId + " petId=" + petId + " photoId=" + photoId + " photoSize=" + photoSize);
PetPhoto petPhoto = petDao.savePetPhoto(accountId, petId, photoId, photoSize, image);
Map<String, Object> map = GsonUtils.wrapWithKeypath(petPhoto, "petPhoto");
return map;
}
Server response JSON (note the keyPath of "petPhoto" that corresponds to the mapping setup):
{
petPhoto = {
accountId = 4ebee3469ae2d8adf983c561;
contentType = "image/png";
filename = "4ebee3469ae2d8adf983c561_4ec0983d036463d900841f09_3FED4959-1042-4D8B-91A8-76AA873851A3";
id = 4ee2e80203646ecd096d5201;
petId = 4ec0983d036463d900841f09;
photoId = "3FED4959-1042-4D8B-91A8-76AA873851A3";
};
}
Delegate:
- (void) objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object {
if ([objectLoader wasSentToResourcePath:#"/pet/uploadPhoto"]) {
PAPetPhoto *photo = (PAPetPhoto*)object;
}
}