I am using RestKit 0.20-rc1 and I am under the impression that in order to set a header value for a request I need to configure a mapping using the #metadata expression. Here is what I have:
RKObjectMapping* mapping = [RKObjectMapping requestMapping];
[mapping addAttributeMappingsFromDictionary:#{
#"etag": #"#metadata.HTTP.request.headers.If-Match"}
];
RKRequestDescriptor* desc = [RKRequestDescriptor requestDescriptorWithMapping:mapping
objectClass:[MyObject class] rootKeyPath:nil];
[[RKObjectManager sharedManager] addRequestDescriptor: desc];
Now what I expect this to do is to set the value in the etag property in MyObject into the If-Match request header. Does this seem right or am I going about this the wrong way? Should I be setting the header directly in the request somehow? If so, how would I go about doing that?
To set a header value for a request, you can set the header of the object manager by setting the HTTPClient like you need it.
[[RKObjectManager sharedManager].HTTPClient setDefaultHeader:#"yourHeaderField" value:#"yourValue"];
Related
I'm attempting to use RestKit to handle posting an updated User object to my remote web service.
Currently my GET requests seem to be working fine but I'm having issues using
[[RKObjectManager sharedManager] postObject:updatedUser path:#"path" parameters:nil success:nil failure:nil];
Invoking this method is throwing a EXC_BAD_ACCESS exception.
My mappings are set up as follows, I believe I have both the RKRequestDescriptor and RKResponseDescriptor's.
User Response Mapping:
RKEntityMapping * userMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([User class])
inManagedObjectStore:[manager managedObjectStore]];
….Setup mapping (I excluded a relationship mapping on this object)
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:userMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"currentUser"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]]
Request mapping:
[manager addRequestDescriptorsFromArray:#[
[RKRequestDescriptor requestDescriptorWithMapping:[userMapping inverseMapping]
objectClass:[User class]
rootKeyPath:nil
method:RKRequestMethodPOST]]];
The mappings seem to set up fine, the EXC_BAD_ACCESS exception is thrown when I call postObject
The test method looks like this, _updatedUser is a CoreData object fetched using [RKObjectManager sharedManager] getObjectsAtPath:…
-(void) doPost{
//this user is a CoreData object fetched using
[_updatedUser setBio:#"This is an update!"];
RKObjectManager * objectManager = [RKObjectManager sharedManager];
[objectManager postObject:_updatedUser
path:#"update/user"
parameters:nil
success:…
failure:…];
}
I've attempted using NSZombies to find the cause of this but I have't had much luck.
From what I can tell the start of the issue seems to be coming from RKObjectParameterization's -[RKObjectParameterization mappingOperation:didSetValue:forKeyPath:usingMapping:] where it looks like everything passed into the method is nil or an empty string.
Thanks!
Much thanks to Wain, after spending way too much time on this the error became instantly apparent after I turned on logging:
RKLogConfigureByName("RestKit", RKLogLevelWarning);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
It turns out I had a circular reference between mapped objects.
I have a one to one relationship where a User contains a UserProfile
I incorrectly set up a bidirectional relationship mapping between User and UserProfile
[userProfileMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"user"
toKeyPath:#"user"
withMapping:userMapping]];
[userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"userProfile"
toKeyPath:#"userProfile"
withMapping:userProfileMapping]];
It looks like my endless loop was caused by userProfileMapping
Thanks Wain, logging lesson learned.
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.
Um beginner with RestKit, first example for me was on foursquare API's and I've used RestKit with Blocks not delegates.
I want to retrive the name's of venues,this is the JSON response
and this is my code:
// App Delegate.m
RKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURLString:#"https://api.Foursquare.com/v2"];
RKManagedObjectStore *objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:#"Venue.sqlite"];
objectManager.objectStore = objectStore;
objectManager.serializationMIMEType = RKMIMETypeJSON;
RKManagedObjectMapping *venueMapping = [RKManagedObjectMapping mappingForClass:[Venue class] inManagedObjectStore:objectStore];
[venueMapping mapKeyPath:#"id" toAttribute:#"id"];
[venueMapping mapKeyPath:#"name" toAttribute:#"name"];
venueMapping.primaryKeyAttribute = #"id";
[objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"response.venue"];
then in myViewController.m
-(void)loadVenues{
// When caling loadObjectsAtResourcePath method it specify RKObjectLoader which is the actual request.
// within these block you can take more options to controll the request.
[[RKObjectManager sharedManager]loadObjectsAtResourcePath:#"/venues/40a55d80f964a52020f31ee3?oauth_token=FNQPN5P5EKLJ5IQ44TMWO00I3W033M0Y1TKINW2OTF2VIOTP&v=20130512" usingBlock:^(RKObjectLoader* loader)
{
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Venue class]];
loader.onDidLoadObject = ^(NSArray *objects)
{
NSLog(#"onDidLoadObject Blocks");
self.data = objects;
[self.tableView reloadData];
};
}
];
}
and the app is entering the block of onDidLoadObject but every time the array is empty !!
even when I test the link on browser it comes with data.
When I debug the loader.URL it always come with these
https://api.Foursquare.com/v2/venues/40a55d80f964a52020f31ee3?v=20130512&oauth_token=FNQPN5P5EKLJ5IQ44TMWO00I3W033M0Y1TKINW2OTF2VIOTP -- https://api.Foursquare.com/v2 -- https://api.Foursquare.com/v2
I don't know why load.URL is wrong ?!
I think um calling the 4square API's with the wrong way, anyone can help ? :)
-Put Your mapping as a class method to be accessed from all application classes, and done only once.
-change the Attribute "id" as it is reserved in Objective-C.
-add this to the block
[loader setObjectMapping:[RestKitMapping yourMapping]];
-then add this with your mapping code
[objectManager.mappingProvider setMapping:venueMapping forKeyPath:#"response.venue"];
-And use Delegates instead of blocks
I've two concerns regarding the above code:
1- Apparently the above JSON response, lists just one Venue .. So KeyPath should be "response.venue" not "response.venues"
2- Where's the mapping for ID? .. which is the primary key that RestKit uses to insert into DB? You need to set the primary key mapping.
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]];