Sending HTTP GET Request using Objective-C - objective-c

I need to querying some data from Parse API. The code below is the example of CURL from Parse :
curl -X GET \
-H "X-Parse-Application-Id: XXXXX" \
-H "X-Parse-REST-API-Key: XXXXX" \
-G \
--data-urlencode 'where={"key1":"value1","key2":"value2"}' \
https://api.parse.com/1/classes/ClassName
Then, this is my code to achieve that :
NSDictionary *dictionary = #{#"key1": #"value1", #"key1": #"value2"};
NSError *error = nil;
NSString *jsonString = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:kNilOptions error:&error];
if (!jsonData)
NSLog(#"Got an error: %#", error);
else
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *fullUrl = [NSString stringWithFormat:#"https://api.parse.com/1/classes/ClassName?where=%#", jsonString];
NSURL *url = [NSURL URLWithString:fullUrl];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
[request addValue:#"XXXXX" forHTTPHeaderField:#"X-Parse-Application-Id"];
[request addValue:#"XXXXX" forHTTPHeaderField:#"X-Parse-REST-API-Key"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSLog(#"Data: %#", data);
NSLog(#"Response: %#", response);
}else{
NSLog(#"Error: %#", error);
}
}];
[task resume];
After executing, i've got some error :
2015-01-16 18:19:57.532 ParseTest[37964:1018046]
Error: Error Domain=NSURLErrorDomain Code=-1002
"The operation couldn’t be completed. (NSURLErrorDomain error -1002.)"
UserInfo=0x7d17d320 {NSUnderlyingError=0x7d051f70 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1002.)"}
What would be the Objective-C equivalent for accomplishing the past CURL code? Or what do you suggest?
Thanks in advance.

NSURLErrorDomain error -1002 means the URL is unsupported. My guess is that you need to URL encode your json for the where argument. Maybe change making your URL to this:
if (!jsonData)
NSLog(#"Got an error: %#", error);
else
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
jsonString = [jsonString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *fullUrl = [NSString stringWithFormat:#"https://api.parse.com/1/classes/ClassName?where=%#", jsonString];
If this doesn't work, have you tried logging fullUrl and try to cURL it?

You need to use a PFQuery. Information for that can be found in Parse's iOS/OS X Documentation.
Here's some sample code for doing what you're attempting above. It should be a direct replacement for that cURL code:
- (void)runQuery {
PFQuery *query = [PFQuery queryWithClassName:#"ClassName"];
[query whereKey:#"key1" equalTo:value1];
[query whereKey:#"key2" equalTo:value2];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error != nil) {
// Do error handling here
} else {
// You have a valid query result
}
}
];
}
Just don't forget that you need to initialize your Parse connection in your app before you can make calls. That way, you don't need to pass your keys with each request, as you do in your cURL code. You'd do that in your AppDelegate object's -didFinishLaunchingWithOptions: method, like so:
#import <Parse/Parse.h>
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize Parse.
[Parse
setApplicationId:#"<App Id>"
clientKey:#"<Client Key>"];
}

Related

NSURLSessionDataTask completionHandler no longer executes when I switch to Release build configuration

I built a simple command line tool that fetches some data using an NSURLSessionDataTask. Now that I'm done coding, I find that the executable hangs when I switch to the Release build configuration in Xcode. I'm using a while loop that waits for the NSURLSessionData completionHandler to complete. The while loop works with Debug, but not with Release. If instead I use [NSThread sleepForTimeInterval:2.0f] to pause the code to wait for the completionHandler to complete, everything works fine in both Release and Debug, however I'd prefer to use the while loop as it is quicker and more logical.
Here is the relevant code:
// Configure Session
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
// Authentication for the session
NSString *auth = [[[NSString stringWithFormat:#"%#:%#",cpanelUser, WxWaPpIUPA] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
config.HTTPAdditionalHeaders = #{#"Authorization":[NSString stringWithFormat:#"Basic %#",auth]};
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
// Run Data Task
__block BOOL taskComplete = NO;
NSURLSessionDataTask *task = [session dataTaskWithURL:urlComponents.URL
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
if(error) {
// *HTTP response failed
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
[result addEntriesFromDictionary:queryReturn(EMAIL_TYPE_ERROR, [NSString stringWithFormat:#"There was a connection issue with the session data task.<br><br>Error: %# HTTP Status Code for the <a href='%#'>URL</a> requested: %li.",error,urlComponents.URL,statusCode])];
} else {
NSError *jsonError;
NSDictionary *jsonObject = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error: &jsonError];
if(!jsonObject){
// *JSON parsing Error
[result addEntriesFromDictionary:queryReturn(EMAIL_TYPE_ERROR, [NSString stringWithFormat:#"There was an error while parsing the JSON data returned from cPanel.<br><br>Error: %#",jsonError])];
} else {
if([jsonObject[#"cpanelresult"] objectForKey:#"error"] != nil) {
// *cPanel API returned an Error
NSString *cpanelError = [NSString stringWithFormat:#"%#",[jsonObject[#"cpanelresult"] objectForKey:#"error"]];
if(verbose) NSLog(#"cPanel API 2 Error: %#\n", cpanelError);
[result addEntriesFromDictionary:queryReturn(EMAIL_TYPE_ERROR, [NSString stringWithFormat:#"cPanel API 2 returned an error while executing function '%#'.<br><br>Error: %#",funct, cpanelError])];
} else {
[result addEntriesFromDictionary:jsonObject];
}
}
}
taskComplete = YES;
}];
[task resume];
// Wait for task to complete
while(!taskComplete);
//[NSThread sleepForTimeInterval:2.0f];

Why is my AFNetworking API call result in an error?

I come from a Swift background; therefore, am used to Alamofire. I am trying to learn Objective-C and have been plying with AFNetworking.
The following is a simple method that calls the Yelp search API to find business in the vicinity.
- (void) getBusinesses: (NSString *) usingToken{
NSString *starbucks = #"Starbucks";
NSString *latitude = #""; //enter latitude
NSString *longitude = #""; //enter longitude
NSString *URLString = [NSString stringWithFormat:#"https://api.yelp.com/v3/businesses/search?term=%#&latitude=%#&longitude=%#", starbucks, latitude, longitude];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
AFHTTPRequestSerializer *request = [[AFHTTPRequestSerializer alloc] requestWithMethod:#"GET" URLString:URLString parameters:nil error:nil];
NSString *bearerToken = [NSString stringWithFormat: #"Bearer %#", usingToken];
[request setValue: bearerToken forHTTPHeaderField:#"Authorization"];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error){
if (error){
NSLog(#"*** Failed!");
} else {
NSLog(#"*** Succeeded!");
}
}];
dataTask.resume;
}
When running this code I get the following error:
Incorrect NSStringEncoding value 0x0000 detected. Assuming NSASCIIStringEncoding. Will stop this compatibility mapping behavior in the near future.
I have tested the above Yelp URL with my valid access token on Postman and it works perfectly.
If someone could point me in the right direction to figure out what is going on I'd appreciate it!
P.S. this is the documentation for the Yelp search endpoint

Soundcloud OAuth request returns always invalid client

Here is my objective-c code to obtain access token from SoundCloud:
- (void) authoriseSoundcloud {
NSString *apiUrl = #"https://api.soundcloud.com/oauth2/token";
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[ NSURL URLWithString:apiUrl]];
NSString * params = [NSString stringWithFormat:#"client_id=%#&client_secret=%#grant_type=password&username=%#&password=%#",client,secretKey,fldUsername.text,fldPassword.text ];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSession *defaultSession = [NSURLSession sharedSession];
NSURLSessionDataTask * dataTask =[defaultSession dataTaskWithRequest:urlRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"Response:%# %#\n", response, error);
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"Data = %#",text);
}
}];
[dataTask resume];
}
However, I always get the result 401- {"error":"invalid_client"}.
However, that client ID works perfectly with those requests, that does not need authorization and I have checked multiple times, that my client ID and secret are correct.
As there is not much samples for iOS to use those parameters in HTTP post body, I assume that maybe my parameters list is incorrect. Any ideas from Soundcloud engineers?
Just a typo mistake, simply add "&" between "client_secret=%#" and "grant_type", like this :
NSString * params = [NSString stringWithFormat:#"client_id=%#&client_secret=%#&grant_type=password&username=%#&password=%#",client,secretKey,fldUsername.text,fldPassword.text ];
Work like a charm :)

Translating cURL request to NSMutableURLRequest

I'm a new Objective-C developer and I'm interacting with an API in the cURL format. I'm used to making calls using URLs, so I pieced together a request from what I found on the internets. I'm still not able to pull the data in my app.
This is the original cURL request (with dummy keys of course):
curl -v -H "app_id:12345" -H "app_key:abcdefg" -X POST "http://data.host.com/object" -d '{"Page":0,"Take":10}'
This is my attempt:
//Request
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://data.host.com/object"]];
//Set method
request.HTTPMethod = #"POST";
//Set parameters
NSDictionary *parameters = #{
#"Page": #(0),
#"Take": #(10)
};
NSMutableString *parameterString = [NSMutableString string];
for (NSString *key in [parameters allKeys]) {
if ([parameterString length]) {
[parameterString appendString:#"&"];
}
[parameterString appendFormat:#"%#=%#", key, parameters[key]];
}
NSLog(#"PARAMETER STRING: %#",parameterString);
//Set headers
[request setValue:#"12345" forHTTPHeaderField:#"app_id"];
[request setValue:#"abcdefg" forHTTPHeaderField:#"app_key"];
[request setHTTPBody:[parameterString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
if ([data length]) {
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"JSON RESPONSE: %#", jsonResponse);
}
} else {
NSLog(#"%#", error);
}
}];
[task resume];
NSLog(#"TASK: %#", task);
I don't get an error, but the jsonResponse returns NULL. Anybody have an idea on what I'm missing? Thanks in advance!
You would see the difference if you compared the HTTP message exchanges between the curl version and your obj-c version. AFAICS you're missing a header for content type where you specify the encoding of the body. When posting you need to pass information on how you are encoding the body.
Here is some example code from one of my apps:
- (NSURLRequest *)createPostRequestWithURL:(NSURL *)url
parameters:(NSDictionary *)parameters {
NSLog(#"startGetTaskForUrl: %#, params %#", url, parameters);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request addValue:#"application/x-www-form-urlencoded"
forHTTPHeaderField:#"Content-Type"];
NSString * httpParams = [self createHttpParameters:parameters];
NSLog(#"HTTPClient: postRequestWithURL body: %#", httpParams);
[request setHTTPBody:[httpParams dataUsingEncoding:NSUTF8StringEncoding]];
return request;
}
- (NSString *)urlEncodedUTF8String: (NSString *) source {
return (id)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(0, (CFStringRef)source, 0,
(CFStringRef)#";/?:#&=$+{}<>,", kCFStringEncodingUTF8));
}
- (NSString *) createHttpParameters: (NSDictionary *) parameters {
NSMutableString *body = [NSMutableString string];
for (NSString *key in parameters) {
NSString *val = [parameters objectForKey:key];
if ([body length])
[body appendString:#"&"];
[body appendFormat:#"%#=%#", [self urlEncodedUTF8String: [key description]],
[self urlEncodedUTF8String: [val description]]];
}
return body;
}

Recreate JSON data in Objective-C

I'm trying to build an app on the Feedly API. In order to be able to mark categories as read, I need to post some data to the API. I'm having no success, though.
This is what the API needs as input:
{
"lastReadEntryId": "TSxGHgRh4oAiHxRU9TgPrpYvYVBPjipkmUVSHGYCTY0=_1449255d60a:22c3491:9c6d71ab",
"action": "markAsRead",
"categoryIds": [
"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/design",
"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/photography"
],
"type": "categories"
}
And this is my method:
- (void)markCategoryAsRead: (NSString*)feedID{
NSLog(#"Feed ID is: %#", feedID);
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *accessToken = [standardUserDefaults objectForKey:#"AccessToken"];
NSString *feedUrl = [NSURL URLWithString:#"https://sandbox.feedly.com/v3/markers"];
NSError *error = nil;
NSDictionary *tmp = [[NSDictionary alloc] initWithObjectsAndKeys:
#"markAsRead", #"action",
#"categories", #"type",
#[feedID], #"categoryIds",
#"1367539068016", #"asOf",
nil];
NSData *postdata = [NSJSONSerialization dataWithJSONObject:tmp options:0 error:&error];
NSLog(#"Postdata is: %#", postdata);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:feedUrl];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request addValue:accessToken forHTTPHeaderField:#"Authorization"];
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *errror = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&errror];
NSLog(#"Response code: %ld", (long)[response statusCode]);
if ([response statusCode] >= 200 && [response statusCode] < 300)
{
NSLog(#"It's marked as read.");
} else {
if (error) NSLog(#"Error: %#", errror);
NSLog(#"No success marking this as read. %#", response);
}
}
It keeps throwing a 400 error though, saying bad input. What am I doing wrong?
You're not doing anything with postdata after creating it. Attach it to the request.
[request setHTTPBody:postData];
There are a few problems in your code. Here are some I noticed:
You're not using postData.
The dictionary you make in tmp doesn't look like the dictionary you said you wanted to send. Where's lastReadEntryId, for example?
NSString *feedUrl should be NSURL *feedUrl
Stylistically, you should be using the dictionary literal syntax to create your dictionary. This will make it easier to debug.