I use RESTKit successfully to access a Web service. But I have problems to map server errors (like 404, 403...) to RESTKit's standard RKErrorMessage class. Let's say I get the following JSON response from my service together with a 403 status code:
{"errors":{"message":"Some error message"}}
In my iOS application I try to map the errors with the help of:
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class
[errorMapping addPropertyMapping:[RKAttributeMapping
attributeMappingFromKeyPath:#"message" toKeyPath:#"message"]];
statusCodes4xx = RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError);
errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:notecardMapping
pathPattern:nil keyPath:#"errors" statusCodes:statusCodes4xx];
But all I get is an error from RESTKit:
Failed mapping operation: No mappable values found for any of the attributes or relationship mappings
Where is my fault? Is it just a wrong path in the descriptor for example?
You seem to be using notecardMapping instead of errorMapping in your response descriptor. Try this:
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"errorMessage"]];
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping
pathPattern:nil
keyPath:#"errors.message"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
[manager addResponseDescriptorsFromArray:#[errorDescriptor]];
Related
I have an entity Comments. When I perform a rest operation, i get a response which has fields such as
{
"status": "succeed"
}
I want to process these fields to know if the operation was successful or not but I dont want to add status to comment class because it doesnt belogn there.
RKObjectManager *sharedRKObjectManager = [RKObjectManager sharedManager];
RKManagedObjectStore *managedObjectStore = [sharedRKObjectManager managedObjectStore];
// Create a mapping for the comment entity
RKEntityMapping *responseMapping = [RKEntityMapping mappingForEntityForName:ENTITY_COMMENT inManagedObjectStore:managedObjectStore];
[responseMapping addAttributeMappingsFromDictionary:#{
#"comment_id": #"commentId"
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
method:RKRequestMethodAny
pathPattern:COMMENT
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Whats the best way to do this?
Just use a plain RKObjectMapping to a custom class or an NSDictionary and with the single status key. Then you should get a simple single item in the mapping result.
I'll try to explain how I want the mapping done:
TOP lvl json object contains Players object which is an array of Player objects
each Player object contains an array, I want each of those objects in the array to be of an Event object (custom object).
now since I have a mapping of the Player object and i'm getting the array filled, tho instead of Event objects(which is what I want), i'm getting NSDictionary objects. thing is that I do have a mapping of my Event class. my issue is getting the RestKit to map these into the array.
I've tried adding responseDescriptors of an Event class tho i've had no luck.
Here is the Player object mapping
RKObjectMapping* playerMapping = [RKObjectMapping mappingForClass:[Player class]];
[playerMapping addAttributeMappingsFromDictionary:#{
...more here
#"activeEvents" : #"activeEvents"
}];
here is the request method
NSURL *taskURL = [NSURL URLWithString:kAppWebApiURLPath];
// Set object manager with base url
RKObjectManager *objectManager = [RKObjectManager sharedManager];
objectManager = [RKObjectManager managerWithBaseURL:taskURL];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager.HTTPClient setDefaultHeader:#"Authorization" value:kAppWebAPIKey];
[objectManager.HTTPClient setDefaultHeader:#"Content-Type" value:#"application/json"];
RKRequestDescriptor * requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[MappingProvider inverseLoginMapping] objectClass:[LoginInfo class] rootKeyPath:nil method:RKRequestMethodPOST];
[objectManager addRequestDescriptor:requestDescriptor];
RKResponseDescriptor *playersResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[MappingProvider playerMapping] method:RKRequestMethodGET pathPattern:nil keyPath:#"players" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:playersResponseDescriptor];
NSLog(#"%#",loginInfo.iOSDeviceToken);
[objectManager postObject:loginInfo path:kAppWebApiLoginPath parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{}...
Update, I now need one step further of mapping, My player object contains an array of events which I successfully mapped using
[playerMapping addRelationshipMappingWithSourceKeyPath:#"activeEvents" mapping:[MappingProvider eventMapping]];
yet now each of those Event objects contains an array of Players, so its like Players -> Events -> Players.
Here is the Mapping for both Event and Player objects :
RKObjectMapping* eventMapping = [RKObjectMapping mappingForClass:[Event class]];
[eventMapping addAttributeMappingsFromDictionary:#{
#"stuffhere" : #"stuffz"
}];
RKObjectMapping* playerMapping = [RKObjectMapping mappingForClass:[Player class]];
[playerMapping addAttributeMappingsFromDictionary:#{
#"name": #"name",
#"activeEvents" : #"activeEvents"
}];
[eventMapping addRelationshipMappingWithSourceKeyPath:#"activeEvents/players" mapping:playerMapping];
now I don't get a recursive function, but how do I state in code to make that relationship
mapping of the json array to assign to my local array property ?
Remove #"activeEvents" : #"activeEvents" from the mapping and replace it with:
[playerMapping addRelationshipMappingWithSourceKeyPath:#"activeEvents" mapping:eventMapping];
You should also only have one response descriptor because the data is nested.
This is my response from API:
{"is_favorited":1}
I want to map it by RestKit but I can't get it working. This I have in AppDelegate.m:
RKObjectMapping *isFavoriteMapping = [RKObjectMapping mappingForClass:[IsFavorite class]];
[isFavoriteMapping addAttributeMappingsFromDictionary:#{
#"is_favorited" : #"Is_favorited",
}];
RKResponseDescriptor *isFavoriteResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:isFavoriteMapping
method:RKRequestMethodAny
pathPattern:#"/api/isPointFavorited/:sid"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[manager addResponseDescriptor:isFavoriteResponseDescriptor];
Path is okay. I have token in address. I am calling it by:
RKObjectManager *manager = [RKObjectManager sharedManager];
[manager getObjectsAtPath: [NSString stringWithFormat:#"/api/isPointFavorited/%#", sid]
parameters:#{
#"pid":[NSString stringWithFormat:#"%#", actualPlace.Id]
}
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
...
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
...
}];
I am using RestKit several times in app and with sid (login token) too. So there is no problem with address or method call. Problem must be in mapping but I don't know how to do it. I tried to change keyPath from nil to #"is_favorited" but it didn't help.
My Is_favorite.h:
#interface IsFavorite : NSObject
#property(nonatomic) NSNumber *Is_favorited;
#end
RestKit error:
Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No mappable object representations were found at the key paths searched." UserInfo=0x1849cbc0 {DetailedErrors=(
), NSLocalizedFailureReason=The mapping operation was unable to find any nested object representations at the key paths searched: events, results
The representation inputted to the mapper was found to contain nested object representations at the following key paths: is_favorited
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}
Edit - changes in AppDelegate but still not working:
RKObjectMapping *isFavoriteMapping = [RKObjectMapping mappingForClass:[IsFavorite class]];
[isFavoriteMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"Is_favorited"]];
RKResponseDescriptor *isFavoriteResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:isFavoriteMapping
method:RKRequestMethodAny
pathPattern:#"/api/isPointFavorited/:sid"
keyPath:#"is_favorited"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[manager addResponseDescriptor:isFavoriteResponseDescriptor];
Remove you usage of addAttributeMappingsFromDictionary:, and change to:
[isFavoriteMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"Is_favorited"]];
then change the keyPath on the response descriptor to #"is_favorited".
This is known as a nil key path mapping (because of the nil source key path in the mapping).
I have the following JSON returned from my restkit call:
{"test":{"123423":{"id":"123423","type":"mood","stress":null}}}
and I am using the following mapping
RKObjectMapping *testResponseMapping = [RKObjectMapping mappingForClass:[TestDataResponse class]];
[testResponseMapping addAttributeMappingToKeyOfRepresentationFromAttribute:#"itemNumber"];
[testResponseMapping addAttributeMappingsFromDictionary:#{
#"(itemNumber).id":#"id",
#"(itemNumber).type":#"type",
#"(itemNumber).stress":#"stress"
}];
RKResponseDescriptor *testResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:testResponseMapping method:RKRequestMethodAny pathPattern:nil keyPath:#"test" statusCodes:[NSIndexSet indexSetWithIndex:200]];
This is not quite right as I'm not sure I am handling the first "123423" (which is a number that changes) correctly.
How do I update my code to accommodate this properly? Thanks!
I have used common RKObjectManager used for different entity mapping as the following below cod but when i try to make mapping for specific entity couldn't because i have two entity with same keyPath this the problem how i can figured.
// Search mapping ...
RKEntityMapping *searchEntityMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([ABB class]) inManagedObjectStore: aBBManager.managedObjectStore];
[searchInfoEntityMapping addAttributeMappingsFromDictionary:#{
#"count" : #"count",
#"total_count" : #"totalCount",
}];
// Search Advanced mapping ...
RKEntityMapping *searchAdvEntityMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([ABB class]) inManagedObjectStore: aBBManager.managedObjectStore];
[searchAdvEntityMapping addAttributeMappingsFromDictionary:#{
#"count" : #"count",
#"data" : #"dataCount",
}];
// Search Descriptor
RKResponseDescriptor *aBBResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:searchEntityMapping pathPattern:nil keyPath:#"locations" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
// Search Adv Descriptor
RKResponseDescriptor *aBB2ResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:searchAdvEntityMapping pathPattern:nil keyPath:#"locations" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
You should use the pathPattern parameter to allow RestKit to know which response descriptor to use when you make a particular request (because you should be using different paths in the URLs for different entities).
If for some reason you can't, you would need to create multiple instances of RKObjectManager and use the appropriate one for each different request that you make.