RKResponseDescriptor in RESTKit is Deprecated - ios

I'm trying to do some RESTKit http requests, and when I use the RKResponseDescriptor line of code, it says 'responseDescriptorWithMapping:pathPattern:keyPath:statusCodes:' is deprecated.
Here is how I coded it:
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil
statusCodes:statusCodeSet];
What exactly is the deal here, and how can I fix it?

Restkit 0.20.3 introduced new feature that allows you to use a response descriptor with multiple requests methods
+ (instancetype)responseDescriptorWithMapping:(RKMapping *)mapping
method:(RKRequestMethod)method
pathPattern:(NSString *)pathPattern
keyPath:(NSString *)keyPath
statusCodes:(NSIndexSet *)statusCodes
So you can just switch to this new implementation.

I had to search a fair bit to figure out what to put for method, so I thought I would share the specifics for others:
RKResponseDescriptor *responseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodAny
pathPattern:nil keyPath:nil
statusCodes:statusCodeSet];
I used the general RKRequestMethodAny, but you can use something more specific if you prefer.

Related

RestKit Collection - The Proper Key Path?

I have the following JSON coming from my server...
{"Devices": [
{
"uuid": "d9084134-d5f7-43a3-9613-7faa769a822a",
"label": "",
"location": "a13d45f4-5ce0-48e3-bc3f-4076bb007037",
"capability": 0
},
{
"uuid": "a4ee0d3f-4a6a-4c61-81bd-3dfa9ab19e85",
"label": "",
"location": "a13d45f4-5ce0-48e3-bc3f-4076bb007037",
"capability": 3
}
]}
I'm trying to map this properly but I'm struggling with it... I have the following mappings setup...
RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:#"Device" inManagedObjectStore:managedObjectStore];
[entityMapping setIdentificationAttributes:[NSArray arrayWithObject:#"uuid"]];
[entityMapping addAttributeMappingsFromDictionary:#{
#"uuid": #"uuid",
#"label": #"label",
#"location": #"location",
#"capability": #"capability"
}];
// GET
RKRequestDescriptor *getRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[entityMapping inverseMapping] objectClass:[Device class] rootKeyPath:#"Device" method:RKRequestMethodGET];
[[RKObjectManager sharedManager] addRequestDescriptor:getRequestDescriptor];
[[RKObjectManager sharedManager].router.routeSet addRoute:[RKRoute routeWithClass:[Device class] pathPattern:#"Devices" method:RKRequestMethodGET]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping method:RKRequestMethodGET pathPattern:#"Devices" keyPath:#"Devices" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
I have my keyPath set to "devices" which I would've thought would work. But RestKit isn't understanding it.
I'm using the following to get the objects from my server...
[[RKObjectManager sharedManager] getObjectsAtPath:#"Devices" parameters:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:#"%d", clientID], #"clientId", #"topic", #"forTopic", nil] success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
...
}... // Abbreviated for brevity.
So I believe my path "Devices" is correct for the pathPattern of "Devices". Are the parameters messing it up? I've had parameters before and it's always worked without having to specify anything in the mapping.
I'm using RestKit 0.27.1 currently.
UPDATE - Fixed JSON
I fixed the JSON as mentioned by Mahendra GP. So this is now a proper array. The RestKit log is now showing the JSON coming through in the trace log. However it still can't identify which object it is (Device).
Snippet of the log...
...and targetObject: (null)
2018-03-09 02:44:05.603734-0700 MyTestApp[31576:6868006] D restkit.object_mapping:RKMapperOperation.m:440 Finished performing object mapping. Results: (null)
2018-03-09 02:44:05.605334-0700 MyTestApp[31576:6868006] E restkit.network:RKObjectRequestOperation.m:172 GET 'http://[MY_IP]:8080/Devices?clientId=5199&forTopic=topic' (200 OK / 0 objects) [request=2.6488s mapping=0.0000s total=2.6633s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors match the response loaded."
I finally figured out my issue by fluke trial and error. I wasn't able to find any specific posts on SO or over on RestKit's site. Therefore I thought I would post the, simple, solution to the problem here.
I kept getting errors that none of my response descriptors matched any of the key paths. I thought it had to do with the parameters I was passing to the GET as it would show that in the error trying to compare it to just the Devices path that the response descriptor was setup with. However this turned out not to be the issue at all.
The issue was with my RKResponseDescriptor setup. I originally had...
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping method:RKRequestMethodGET pathPattern:#"Devices" keyPath:#"Devices" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
The solution here is the "pathPattern". This needed to be set to nil.
I've been using RestKit for many many years. Prior to the 0.2x releases. I remembered something about RestKit being able to identify an entity and it's multiple just by pluralizing it. I finally thought that perhaps because I was specifying it in the pathPattern it was causing the issue. So once I eventually set that to nil it finally started working. I actually think the reason why was that setting it to nil was like setting it it to a wildcard for any pathPattern since my path actually had parameters tagged onto them.
However in another use case I had I did specify the pathPattern but instead of it being the plural of the mapped entity it was something different. So instead of Devices I had something like DeviceForUser. So there seems to be a problem when specifying the plural of the entity in the pathPattern. Especially if using parameters in the getObjectsAtPath call.
So for clarity, all of the code above is the same. It just needs this change...
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping method:RKRequestMethodGET pathPattern:nil keyPath:#"Devices" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
I hope this helps anyone else who might run into this.

Object mapper for xml in RestKit

I am trying map error response from server xml. In version 0.21 of restkit it worked ok. In last version it did not (0.22 and above). Possible response from server is <authorization-fail/>. Many server functions can lead to this response.
+ (void)addAuthErrorMapping:(RKObjectManager*)objectManager
{
RKObjectMapping* errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil
toKeyPath:#"errorMessage"]];
RKResponseDescriptor* errorResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"authorization-fail"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:errorResponseDescriptor];
}
In 0.21 version I am getting dictionary with authorisation-fail key. In last versions result is empty dictionary.
This should never have worked.
The server should be returning the response to you with a non-2xx HTTP response code, and you should be using that code to trigger your response descriptor.
As it is, you're triggering the response descriptor all the time and telling it to look for - and drill into - the authorisation-fail. So, if that key exists but is empty you should get an RKErrorMessage that's empty.
If you have some other response descriptor that matches the response before this one (i.e. any other with a nil path pattern and success response code) then this one will never work and that is why you're getting an empty dictionary.
So, the best option is to change the server response, and the workaround solution is to use a dynamic mapping which checks the content and provides either a good response or an error mapping to be used.

RestKit .20 RKRequestDescriptor postObject - Expected status code in (400-499), got 200

Having a bit of a strange problem, I'm using RestKit to post a CoreData object to a remote web service. Everything on the remote end looks to be working fine but it seems like RestKit is having some issues mapping the response.
My object response mappings look like this, I have a User class with a UserProfile one to one relationship:
//UserProfile
RKEntityMapping * userProfileMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([UserProfile class])
inManagedObjectStore:[manager managedObjectStore]];
//…mapping
//User
RKEntityMapping * userMapping =
[RKEntityMapping mappingForEntityForName:NSStringFromClass([User class])
inManagedObjectStore:[manager managedObjectStore]];
//…mapping
//relationship mapping
[userMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"userProfile"
toKeyPath:#"userProfile"
withMapping:userProfileMapping]];
//add the response mappings for GET success
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:userMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"currentUser"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]] …rest of my mappings];
The request mapping is the inverse of userMapping
RKRequestDescriptor * currentUserRequestDescriptor =
[RKRequestDescriptor requestDescriptorWithMapping:[userMapping inverseMapping]
objectClass:[User class]
rootKeyPath:#"currentUser"
method:RKRequestMethodPOST];
[manager addRequestDescriptor:currentUserRequestDescriptor];
My GET requests on the User class are working fine, POST is also working correctly.
To update my User coreData object I'm using `postObject'
[objectManager postObject:updatedUser
path:#"update"
parameters:nil
success:…handler
failure:…handler]
The POST works correctly and the object is saved in my remote service.
However, my success handler is never called and I never get the updated User object. I have logging on and I see the valid JSON in the response.
response.body={"currentUser":{….user object}}
Instead, failure is called, it seems to want a 400 response instead of the 200 my service is responding with.
"NSLocalizedDescription" -> "Expected status code in (400-499), got 200"
I don't see a way to set the expected statusCodes on RKRequestDescriptor like on RKResponseDescriptor.
Thanks
Because your User response descriptor uses method:RKRequestMethodGET so it won't be considered as a match for the response from your POST request.
Instead, set the method to RKRequestMethodAny.
Presumably RestKit is expecting a 400-499 because you have a response descriptor configured to handle error responses at that path.

Can I use RestKit 0.20.3 to submit a request with OAuth parameters required by Yelp?

I am writing an iOS app that requests data from Yelp.
I currently have code that manages Yelp requests/responses and parses the JSON data returned internally. The current code builds the OAuth parameters for its requests using OAuthConsumer by Jon Crosby.
I came upon RestKit yesterday, and found it very appealing. It would probably eliminate much of the request submission and response parsing I am doing.
But I hit a roadblock, because I have not been able to figure out how to generate the OAuth parameters that Yelp requires with RestKit. I worked through the Ray Wenderlich RestKit tutorial at http://www.raywenderlich.com/13097/intro-to-restkit-tutorial, but it uses a client ID and client secret (as required by Foursquare).
Yelp requests need to have a consumer key, token, signature method, signature, timestamp and nonce. I have been unable to find an add-on for RestKit that can generate this particular set of OAuth parameters.
I am now generating my RestKit GET requests using an AFOAuth1Client developed by Matt Thompson. Now the Yelp API is returning an Invalid Signature error when I send a request.
I am puzzled because I have checked the fields in the HTTP Authorization header, and they look correct. The error seems to indicate that Yelp wants the oauth parameters in the URL, but the API documentation says that it is acceptable to send them in the authorization header.
Here is the Invalid Signature error I'm getting:
2013-08-26 15:34:54.806 RestKitYelpGroupon[2157:400b] E restkit.network:RKObjectRequestOperation.m:575 Object request failed: Underlying HTTP request operation failed with error: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (200-299), got 400" UserInfo=0xa876190 {NSLocalizedRecoverySuggestion={"error": {"text": "Signature was invalid", "id": "INVALID_SIGNATURE", "description": "Invalid signature. Expected signature base string: GET\u0026http%3A%2F%2Fapi.yelp.com%2Fv2%2Fsearch\u0026ll%3D32.893282%252C-117.195083%26oauth_consumer_key%3D(MY CONSUMER KEY)%26oauth_nonce%3DC0F25D91-B473-4059-B5F6-2D850A144A1D%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1377556409%26oauth_token%3D(MY OAUTH TOKEN)%26oauth_version%3D1.0%26term%3Dsushi"}}, AFNetworkingOperationFailingURLRequestErrorKey=http://api.yelp.com/v2/search?ll=32.893282%2C-117.195083&term=sushi>, NSErrorFailingURLKey=http://api.yelp.com/v2/search?ll=32.893282%2C-117.195083&term=sushi, NSLocalizedDescription=Expected status code in (200-299), got 400, AFNetworkingOperationFailingURLResponseErrorKey=}
Here is the code I am using to generate the requests:
NSURL *baseURL = [NSURL URLWithString:#"http://api.yelp.com/v2"];
AFOAuth1Client *oauth1Client = [[AFOAuth1Client alloc] initWithBaseURL:baseURL key:consumerKey secret:consumerSecret];
oauth1Client.accessToken = [[AFOAuth1Token alloc] initWithKey:tokenKey
secret:tokenSecret
session:nil
expiration:[NSDate distantFuture]
renewable:NO];
[oauth1Client registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[oauth1Client setDefaultHeader:#"Accept" value:#"application/json"];
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:oauth1Client];
RKObjectMapping *couponMapping = [RKObjectMapping mappingForClass:[Coupon class]];
[couponMapping addAttributeMappingsFromDictionary:#{#"id" : #"id"}];
RKObjectMapping *businessMapping = [RKObjectMapping mappingForClass:[Business class]];
[businessMapping addAttributeMappingsFromDictionary:#{#"name" : #"name"}];
[businessMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"deals" toKeyPath:#"location" withMapping:couponMapping]];
RKResponseDescriptor * responseDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping:businessMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"response.venues"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
[objectManager getObjectsAtPath:#"http://api.yelp.com/v2/search"
parameters:queryParams
success:^(RKObjectRequestOperation * operaton, RKMappingResult *mappingResult)
Any further assistance is greatly appreciated!
Take a look at using AFOAuth2Client and setting it as the client when you init your RKObjectManager.

RKRoute with "/" as parameter doesn't work

I'm trying to set up a RKRoute to get folder contents from the Dropbox API using RestKit.
The URL to get the contents is https://api.dropbox.com/1/metadata/dropbox/<path>.
So I set up the response and route like this:
// objectManagers baseURL is #"https://api.dropbox.com/1/"
RKResponseDescriptor *rootResponse = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping pathPattern:#"metadata/dropbox:path" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[DropboxFolder class] pathPattern:#"metadata/dropbox:path" method:RKRequestMethodGET]];
// dropboxFolder.path is #"/" for root
// dropboxFolder.path is #"/Images" for the "Images"-folder contained in root
But then matching of the path fails in [RKPathMather matchesPath:tokenizeQueryStrings:parsedArguments: because there the number of slashes are checked like this: RKNumberOfSlashesInString(self.patternString) == RKNumberOfSlashesInString(self.rootPath)
The mapping works, when I comment out this checking, but I'm sure it's needed in some other cases.
The correct approach is to use 2 different response descriptors and routes. The slashes are important to allow RestKit to properly differentiate between different URLs, paths and patterns. You can use the same mapping so it's 'just' another couple of lines of configuration.
I think I found out a cleaner solution for my problem:
I configured my response descriptor and route like this:
RKResponseDescriptor *rootResponse = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping pathPattern:#"metadata/dropbox/:path" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKRoute *route = [RKRoute routeWithClass:[DropboxFolder class] pathPattern:#"metadata/dropbox/:path" method:RKRequestMethodGET];
route.shouldEscapePath = YES;
[objectManager.router.routeSet addRoute:route];
Now it works with 1 response descriptor and 1 route. I don't know if this works with other APIs using paths but it does with Dropbox. (The URL for root is now https://api.dropbox.com/1/metadata/dropbox/%2F)

Resources