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.
Related
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)];
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.
I have spent the last few hours trying a number of different solutions to this problem by looking at similar similar problems that have been resolved on StackOverFlow threads.
However, I have hit a brick wall.
Some background.
Using Restkit 0.22 and Core Data to Persist 2 Objects and set up a relationship between them.
The two Objects that I am modelling are as follows, Relationships are also included in the form of a One-One relationship between COAgendaItem and COMapItemLocation (The relationship that isn't working at present) and a One-Many between COMapItemLocation and COAgendaItem (I have not looked at mapping this yet):
-- COAgendaItem
-- COMapItemLocation
They have the Corresponding Attributes and Relationships
The desired result is to have the core data relationship working alongside RestKit, i.e. The property agendaItemLocation accessible via code on a COAgendaItem object.
The JSON Response from the Server looks like this
The above JSON Response is the data required for a COAgendaItem object plus the primary key of a COMapItemLocation Object. I am having a problem mapping the location_id field which is the primary key of a COMapItemLocation. (I have the Response Descriptors and the retrieval code set up and it is working correctly, it has only become a problem since I attempted to map a relationship)
So far I have modified my RKEntityMapping to try and map the relationship.
+ (RKMapping *)agendaItemMapping{
RKEntityMapping *agendaItemMapping = [RKEntityMapping mappingForEntityForName:#"COAgendaItem" inManagedObjectStore:[[COPersistenceCoordinator sharedCOPersistenceCoordinator]restKitObjectStore]];
[agendaItemMapping addAttributeMappingsFromDictionary:#{#"id": #"agendaItemID",#"title": #"agendaItemTitle",#"description": #"agendaItemDescription",#"start": #"agendaItemStartTime",#"end": #"agendaItemEndTime",#"category": #"agendaItemCategory",#"location_id" : #"agendaItemLocation"}];
[agendaItemMapping addConnectionForRelationship:#"agendaItemLocation" connectedBy:#"location_id"];
return agendaItemMapping;
}
However this causes a crash on launch as shown below (Note, I have added the #"location_id" : #"agendaItemLocation" mapping for the foreign key in the default mapping as well, is this necessary)
I have tried modifying the [agendaItemMapping addConnectionForRelationship:#"agendaItemLocation" connectedBy:#"location_id"];
Statement with all the combinations I can think of both in single string and dictionary formats i.e. #{"agendaItemLocation" : #"location_id"} but whatever I try, it always ends up in the same error as before 'Cannot connect relationship: invalid attributes given for source entity 'COAgendaItem': location_id'.
I am completely stumped on this one, can anyone point anything that I have done wrong or need to do differently?
Thanks greatly for any help in advance! :)
You describe 2 different relationships, but really you just have one with an inverse. This is correct, it's just your description which is wrong (and thus probably your understanding of setting the relationship contents).
The connection cannot be made (and you get an error) because RestKit needs to know the id of the other object to find (to connect to). You're telling it to use location_id, but it needs to be a property on the class (usually transient), not an item in the JSON (because the connection is made after the JSON has been processed and gone).
Add a property to the entity and a mapping to place the location_id into it. Update the connection with the name of that property.
I'm having trouble mapping a particular JSON structure, as illustrated in a simplified form here:
{"personDetails":{"eyeColor":"brown",
"height":"2m 12cm",
"specialRestrictions":null,
"person":{"personId":42,
"firstName":"Hummingbird",
"lastName":"Collins",
"dob":1360856245000,
"gender":"F",
"personCode":"8DECCC6D-68CA-47E1-AV7F-84C2039D517",
"isAdmin":false}
}
}
In this case, I'd like to use the "personId" field, or even the "person" object itself, as a primary key of my "personDetails" object. As far as I can tell, there is no way to do this. I looked into RKConnectionDescription, but it doesn't seem applicable in this case, since the entire object is embedded, not just a foreign key attribute.
I basically want the relationship to be one-to-one, in that, when I call my service, like this,
http://server/services/getPersonDetailsByID/42
the details can map and persist in CoreData, overwriting the PersonDetails for that Person that were previously saved. At the moment, multiple PersonDetails objects can exist for the same Person locally, because there is no key in place.
So, my question is this: can RestKit mapping be set up to accomplish this intended behavior? Or will I need to handle the deletion of any outdated CoreData objects myself?
Edit:
Here's how my mapping looks currently.
RKEntityMapping* personDetailsMapping = [RKEntityMapping mappingForEntityForName:
#"personDetails" inManagedObjectStore:objectManager.managedObjectStore];
[personDetailsMapping addAttributeMappingsFromArray:#[#"eyeColor", #"height",
#"specialRestrictions"]];
[personDetailsMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"person" toKeyPath:#"person"
withMapping:personMapping]]; // personMapping defined earlier
Here are two different ways I've tried adding an Identification attribute:
personDetailsMapping.identificationAttributes = #[#"person"];
and
personDetailsMapping.identificationAttributes = #[#"personId"];
at different times, and they each throw an error like this:
Invalid attribute 'personId': no attribute was found for the given name in the 'PersonDetails' entity.
It's possible that adding the key path as the identification attribute will work. I haven't actually tried it but key paths work in most places.
personDetailsMapping.identificationAttributes = #[ #"person.personId" ]
If it doesn't, you can setup your mapping to copy the person id into the details object. You will need to add a new persistent attribute and then you use the key path in the mapping:
#"personId" : #"person.personId"
Very confusing times here adding various requests to a project in development. For two objects so far setObjectMapping:forResourcePathPattern: works fine. These have resource patterns like #"customer/login/" and #"customer/getDuckStatus/". A query that fails to create an object with correct mapping is #"customer/getPondsForDuck/"
// this define is used for object mapping and for creating the query,
// it can't be a mismatch
#define kResourcePathDuckPond #"customer/getPondsForDuck/"
The only difference I can see in the queries is that the keypath in the returned JSON is nil for the first two queries that work. When the keypath is nil and forResourcePathPattern is used to set object mapping, objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects (RKObjectLoaderDelegate method) receives a valid object with correct mapping. When the keypath is non-nil, using forResourcePathPattern always sends didLoadObjects: a nil object.
Using
[[RKObjectManager sharedManager].mappingProvider setObjectMapping:duckMapping
forResourcePathPattern:kResourcePathDuckPond];
fails. Using
[[RKObjectManager sharedManager].mappingProvider setObjectMapping:duckMapping
forKeyPath:#"ponds"];
(where "ponds" is the keypath given in the returned JSON) works fine.
Is that the rule, forResourcePathPattern will fail unless keypath is nil? I'd like to have all my objects recognized the same way. While I can get it working I don't like using key path for one query and resource path for others. I wanted to use forResourcePathPattern for everything since I don't have control of the API and some queries return a keypath while some don't.