3

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!

Carl Smith
  • 1,065
  • 13
  • 18

1 Answers1

0

Take a look at using AFOAuth2Client and setting it as the client when you init your RKObjectManager.

Wain
  • 117,132
  • 14
  • 131
  • 151
  • Yelp's API uses OAuth 1.0a, so I tried using AFOAuth1Client to generate my requests. The values in the HTTP Authorization Header look correct, but the Yelp API is returning an Invalid Signature error. I will update my question with the code I'm using and the error text. – Carl Smith Aug 26 '13 at 22:52