Post path must match exactly with RKResponseDescriptor pathPattern - ios

So the current service I'm using for updating an object follows the formatting of the following URL
http://www.baseurl.com/servicemethodAddObject/transactionId?clientTime=clientTime
So this is the code I put in there. Roughly.
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:eventResponseMapping method:RKRequestMethodGET pathPattern:#"/GetStuff" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
// I'd want to add items to the URL but the parameters parameter only goes in to the post body, If I add objects to the path #"?GetStuff/?parm=Stuff&parm2=stuff2" then the response will not be mapped properly
[objectManager postObject:object path:#"/GetStuff" parameters:parms success:nil failure:nil];
It seems that RestKit requires that the path used to post the object and the pathPattern used to create the RKDescriptor must be an exact match. Is there a way to get restKit to do a comparison that ignores the url parameters for it's object mapping?
Is this even the right thing to do? Posting an object and adding items in the URL.

You can build the path to add your parameters. Using pathPattern:#"/GetStuff" will match against built paths like:
#"/GetStuff?parm=Stuff&parm2=stuff2"
(you will have issues if you add or remove / characters that aren't in your path pattern)

https://github.com/RestKit/RestKit/issues/1877
Apparently the solution is to not do it.

Related

iOS RestKit: How to make sure POST parameters are encoded in the request URL?

I'm trying to use RestKit because I'm expecting to make Core Data managed objects out of requests responses and it seemed like the framework was all about doing that and it seemed to be rather full featured.
But I'm having trouble getting my POST /user/login with parameters api_key=<value> (plus a separate JSON body) to end up a going out in a request like /user/login?api_key=<value>.
In the internals of RKObjectManager, requestWithMethod:path:parameters: does:
// NOTE: If the HTTP client has been subclasses, then the developer may be trying to perform signing on the request
NSDictionary *parametersForClient = [self.HTTPClient isMemberOfClass:[AFHTTPClient class]] ? nil : parameters;
request = [self.HTTPClient requestWithMethod:method path:path parameters:parametersForClient];
Do I have it right that this AFNetworking superclass method encodes parameters into the URL query? And does this mean the only way to ensure parameters are passed to that is to have my RKObjectManager use some subclass of AFHTTPClient?
And, according to the comment, supposedly this is only for sake of maybe a fringe case, something about request signing or something? Why is URL query-encoded request parameters not a common thing to do??
And getting things JSON encoded like I want does not seem to be as easy as I'd hoped either. Maybe it's a mistake for me to even try to use RestKit.

Restkit How To Set Value Outside Of Object Mapping [duplicate]

is there a way to map the parameters in an URL to the results?
I got a rest service where the user can search something by its ID. The path is search/:id
The results contains the name and other properties but not the ID.
I could do something like this:
NSArray *array = mappingResult.array;
for (Item *item in array) {
[item setId:itemID];
}
but I hope there is a nicer way...
Thanks for any hints
Xean
You want to use the path pattern you specify in your response descriptor. Then you want to use routing (RKRoute) and metadata during your mapping. The metadata includes a routing section which gives access to the parameters extracted from the URL path.
Some info on metadata here (the docs are a little lacking).
In your mapping you want to use:
#metadata.routing.parameters.id
As the mapping source key path.
To make routing work you need to add the route to your object manager:
[manager.router.routeSet addRoute:...
And then you need to make the request in a way that means the route is used, like getObjectsAtPathForRouteNamed:object:parameters:success:failure:.
You probably need to specify the pathPattern and keyPath of your response descriptor::
RKResponseDescriptor *responseDescriptor =
[RKResponseDescriptor
responseDescriptorWithMapping:itemMapping
method:RKRequestMethodAny
pathPattern:#"search/:id"
keyPath:#"item"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

Seeking recommendations for best RestKit/CoreData mapping and JSON structure for shallow routes

I'm developing a CoreData iOS app that's backed by a (Rails) REST API (that supports shallow routes). Because there are a lot of objects in the graph, I'd like the REST GETs to not to include a lot of nested results and, instead, just contain references that RestKit uses to establish (faulted) relationships. I'll then use shallow routes to request the individual (or groups of) objects as needed.
Assuming I have a one-to-many (<-->>) data model such as A <-->> B, I have:
RKEntityMapping *a_mapping = [RKEntityMapping mappingForEntityForName:#"A" inManagedObjectStore:managedObjectStore];
[a_mapping addAttributeMappingsFromDictionary:#{#"a_id" : #"aId", ...}];
a_mapping.identificationAttributes = #[#"aId"];
RKEntityMapping *b_mapping = [RKEntityMapping mappingForEntityForName:#"B" inManagedObjectStore:managedObjectStore];
[b_mapping addAttributeMappingsFromDictionary:#{#"b_id" : #"bId", ...}];
b_mapping.identificationAttributes = #[#"bId"];
[a_mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"bs" toKeyPath:#"bs" withMapping:b_mapping]];
I have these routes:
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *a_ResponseDescriptor;
a_ResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:a_mapping method:RKRequestMethodGET pathPattern:#"/A/:aId" keyPath:#"A" statusCodes:statusCodes];
RKResponseDescriptor *b_ResponseDescriptor;
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
b_ResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:b_mapping method:RKRequestMethodGET pathPattern:#"/B/:bId" keyPath:#"B" statusCodes:statusCodes];
[[RKObjectManager sharedManager] addResponseDescriptor:a_ResponseDescriptor];
[[RKObjectManager sharedManager] addResponseDescriptor:b_ResponseDescriptor];
I have a couple of related questions:
How should I structure the JSON when returning an 'A' record so that RestKit will instantiate stubs for any related 'B' objects?
Similarly, if I want to request a bunch of B objects (without prior knowledge of A objects) how do I structure the JSON when returning a 'B' record so that RestKit will instantiate stubs for the owning 'A' object?
What additional setup/code do I need with RestKit?
Currently, I have one direction working (A --> B), but I can't seem to figure out how to get the reverse to work. In particular, /A/1.json returns something like:
{"a": {"a_id":1, "bs":[{"b_id": 2}, {"b_id": 3}]}}
And B/2.json returns:
{"b": {"b_id":2, "a_id": 1}}
Should I instead be using something like:
{"b": {"b_id":2, "a": {"a_id": 1}}}
? Any help/advice would be appreciated.
Faults are a Core Data concept. RestKit is involved with creating objects in the data store, but it is the act of fetching them from the data store which faults them. It sounds more like what you're interested in is having RestKit create 'stub' objects in the data store (objects with the correct id and relationship but no detailed attributes set) so they can be filled in later when required.
Your current mappings and JSON for /A/1.json are fine for creating a stub instance of A and stubs of the connected Bs.
B/2.json would only create a stub B (it wouldn't do anything with A as the mapping has no relationship information attached). It's the mapping that is at 'fault', not the JSON. Add an RKRelationshipMapping to the mapping for B and the stub would be created and connection made.
That said, you would not usually be making a request for B which required 'back-stubbing' of A, because you would usually need to have requested A in order to get B.
Finally, you don't need different mappings for stubs and detail. Just have 1 mapping which contains all the detail and id mappings and RestKit will do everything that it can based on the received JSON (so if the JSON only contains ids you will just get stub objects).
To build off of what Wain said, the solution is to:
establish bidirectional RKRelationshipMappings
use mappings with nested mappings when sending JSON for both A and B objects. Ex. {"b": {"b_id":2, "a": {"a_id": 1}}}
Also, if you're working with the iOS simulator, be sure to set your HTTP response headers to not cache things. This nailed me a bunch of times since my code was using stale data.

RestKit not mapping objects because of another object mapping

I have two objects, item and category. I have concrete urls for most queries so the mapping is fine. But for certain item request I have dynamic mapping that changes URLS and it looks like this:
[RKResponseDescriptor responseDescriptorWithMapping:goodMapping
pathPattern:nil
keyPath:nil
statusCodes:statusCodes]
If this is present, my Categories fail to map, although this is item mapping. If I comment this line out everything works fine.
My problem is that I can't set pathPattern, because even if I set the path to actual path it fails to detect it, here is the error I get:
failed to match: response path 'catalog/item?category_id=2' did not match the path pattern 'catalog/item?category_id=2'
I tried replacing "2" with ":id" to no avail. I can't set keyPath either, because I am getting unnamed array from server.
How do I change my mapping so it mapped my objects and detected them?
I managed to make it work by exactly matching pathPattern to request url, ignoring all get parameters, so it was "catalog/item" in request and "catalog/item" in mapping and everything worked, even if the resulting url is catalog/item?category_id=2.

RestKit 0.20.0-rc1 - Duplicate object after POST

I'm running into an issue while trying to synchronize a list of favourited teams for a given user between my iOS app and my server. The flow of events is as follows:
User favourites a team
New favouriteTeam object is created and saved to Core Data:
NSError *error;
[[self.currentUser managedObjectContext] save:&error];
[[RKManagedObjectStore defaultStore].persistentStoreManagedObjectContext save:&error];
Array of modified favouriteTeams is POSTed to the server where they are timestamped and returned with any other modified or recently added (by another device) objects.
The problem I'm running into is that the item that is POSTed, since it is always returned is being duplicated instead of being overwritten (based on the identificationAttributes). None of the other objects returned (whether modified or newly created by another device) get duplicated... just the newly created device from the user's device.
Here's my Request/Response mapping code:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[objectManager setRequestSerializationMIMEType:RKMIMETypeJSON];
// POST Request Mapping
RKObjectMapping *favouriteTeamMapping = [RKObjectMapping requestMapping];
[favouriteTeamMapping addAttributeMappingsFromArray:#[#"uuid", #"teamName", #"displayOrder"]];
RKRequestDescriptor *favouriteTeamRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:favouriteTeamMapping objectClass:[FavouriteTeam class] rootKeyPath:#"favouriteTeams"];
[objectManager addRequestDescriptor:favouriteTeamRequestDescriptor];
// Response Mapping
RKEntityMapping *favouriteTeamResponseMapping = [RKEntityMapping mappingForEntityForName:#"FavouriteTeam" inManagedObjectStore:objectManager.managedObjectStore];
favouriteTeamResponseMapping.identificationAttributes = #[#"uuid"];
[favouriteTeamResponseMapping addAttributeMappingsFromArray:#[#"uuid", #"teamName", #"displayOrder", #"lastModified"]];
RKResponseDescriptor *favouriteTeamResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:favouriteTeamResponseMapping pathPattern:#"/api/favouriteteam/" keyPath:#"data.favouriteTeams" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptorsFromArray:#[favouriteTeamResponseDescriptor]];
// POST
[objectManager postObject:favTeamsArray path:#"/api/favouriteteam/" parameters:nil success:nil failure:nil];
I can't seem to figure out why this duplication is happening when the identificationAttributes (favouriteDrug "uuid" attribute) are set. The objects are identical (even the same uuid) in Core Data. What is causing this behaviour?
NOTE: This question is also posted on the RestKit Google Groups here.
Thanks!
UPDATE: It appears that this duplication doesn't occur the first time a team is favorited. Any of the following attempts to favorite a team result in this duplication.
I had a similar issue, and while I cannot really confirm if this addressed your problem, will post my answer here anyways.
My problem was that the first object would be created fine, but thereafter restkit would save duplicate objects in coredata, so this sounds like the problem you are seeing.
I was making the post call to create object by:
* creating a blank object in coredata and filling it with whatever attributes I need
* make a post call to server using restkit API and passing in the newly created object
What restkit does under the hood is to take the response, fill it into the new object I created, and save it into coredata WITHOUT checking if there is another object of the same unique id beforehand. This is why the first object creation was fine, but subsequent objects were duplicates.
The way I solved it was actually to pass in the raw values as params to the restkit post API call, and nil as the object. On the reply, restkit will then look through coredata first to see if an object of that ID exists and merge changes with that object, or create a new one.

Resources