I am working with GitHub's REST API for iOS and am having trouble getting it to work correctly with basic authentication. I have written the following code to view a users GitHub repositories:
NSString *requestString = [NSString stringWithFormat:#"https://%#:%##api.github.com/user/repos",userName,password];
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *strData = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", strData);
}];
[dataTask resume];
I am getting the following response back:
{"message":"Requires authentication","documentation_url":"https://developer.github.com/v3"}
However when I do the following in my terminal:
curl -i GET https://userName:password#api.github.com/user/repos
I get the expected result back from GitHub. I'm not sure what authentication I am missing for my iOS app because I don't have anything in my curl statement that I don't have in my iOS code. Any help explaining what I'm missing is greatly appreciated.
It seems that something in NSURLSession doesn't like the username/password encoded in the URL (and to be honest, it isn't very secure as it can be exposed in proxy logs and is also prone to problems on the server side), so you have to encode the authorisation details into the HTTP headers.
This worked for me -
NSString *requestString = #"https://api.github.com/user/repos";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSData *userPasswordData = [[NSString stringWithFormat:#"%#:%#", userName, password] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];
NSString *authString = [NSString stringWithFormat:#"Basic %#", base64EncodedCredential];
NSURLSessionConfiguration *sessionConfig=[NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.HTTPAdditionalHeaders=#{#"Authorization":authString};
self.session=[NSURLSession sessionWithConfiguration:sessionConfig];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *strData = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", strData);
}];
[dataTask resume];
I can see two possible reasons:
your password contains special characters that needs to be "percent escaped": see "NSString method to percent escape '&' for URL"
the ## is somehow misinterpreted: you can try to POST the username and password as in this question
Related
I am fetching JSON values from the API...but it showing the values as null... I checked it in the postman it's working there and I have the response...I have listed my sample code below please mention my mistakes. I'm new to this development
- (IBAction)PaymentButtonTapped:(id)sender {
[self PayuMoneyAdminValues];
}
-(void)PayuMoneyAdminValues{
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc]init];
NSString *urlstring = [NSString stringWithFormat:#"%#admindatas",restSiteURLServices];
NSURL *url = [NSURL URLWithString:urlstring];
NSString *userUpdate = [NSString stringWithFormat:#"%#",restAuth];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setURL:url];
//Convert the String to Data
NSData *data1 = [userUpdate dataUsingEncoding:NSUTF8StringEncoding];
//NSData *data1 = [NSData dataWithContentsOfURL:url];
//Apply the data to the body
[urlRequest setHTTPBody:data1];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(data!=nil){
NSError *parseError = nil;
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (parseError) {
NSLog(#"Admin data: %#",responseDictionary);
}
}
}];
[dataTask resume];
}
I'm using the Open Weather Map API to fetch the user's current weather.
When I run the request in Postman, I get a status code 200 and am able to receive data. However, in Xcode I get the error -1002
Here is the code executing the 'GET' request:
// Configure URL String
NSString *apiKey = #"MY_API_KEY";
NSString *urlString = [[NSString alloc] initWithFormat:#"api.openweathermap.org/data/2.5/weather?APPID=%#&lat=%#&lon=%#", apiKey, latitude, longitude];
NSString *encodedUrl = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL *url = [NSURL URLWithString:encodedUrl];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
[theRequest setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:theRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(data != nil) {
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(#"Here is the json dict: %#", jsonDict);
NSLog(#"%#", response);
}
else {
NSLog(#"error: %#", error);
}
}] resume];
Again, the request here returns the error but in Postman I get no error when I run:
api.openweathermap.org/data/2.5/weather?APPID=MY_API_KEY&lat=33.712926&lon=-117.839181
What is the issue with how I'm setting up the request?
A URL needs a scheme part. Please make sure you include a protocol such as http or https in your URL string:
NSString *urlString = [[NSString alloc] initWithFormat:#"https://api.openweathermap.org/data/2.5/weather?APPID=%#&lat=%#&lon=%#", apiKey, latitude, longitude];
To my understanding, to use this website I have to convert an image to a base64 encoded image and then send it to this website. The website will then send me back a number (as a decimal).
https://docs.indico.io/docs/rest-api-image-analysis
I've tried using a number of steps, namely trying to alter a similar process used for sending text and receiving a number. Any tips?
UPDATE:
- (IBAction)press:(id)sender {
//UIImage *imager = photos.image;
NSData *imageData = UIImageJPEGRepresentation(photos.image, 1.0);
NSString *base64Img = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//not sure if #"data" or #"data.json" and whether the base64img should be behind it
NSDictionary *parameters = #{#"data":base64Img};
NSMutableString *parameterString = [NSMutableString string];
for (NSString *key in [parameters allKeys]) {
if ([parameterString length]) {
[parameterString appendString:#"&"];
}
[parameterString appendFormat:#"%#=%#", key, parameters[key]];
NSURL *url = [NSURL URLWithString:#"http://apiv2.indico.io/contentfiltering?key='17767cb46eb4b4f568832be2c953022b"];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[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];
//GET RESULT;
NSLog(#"A %#", parameters[#"results"]);
}
} else {
NSLog(#"%#", error);
}
}];
[task resume];
}
the results I get usually return as (null)
maybe try going
NSDictionary *parameters = #{#"data":base64Img};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters
options:nil
error:&error];
//do some error checking
NSURL *url = [NSURL URLWithString:#"http://apiv2.indico.io/contentfiltering?key='17767cb46eb4b4f568832be2c953022b"];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:jsonData];
//...
just doing this off the top of my head so may need some tweaking
If I had to guess it looks like you've accidentally added a typo to your API key. In the line above:
http://apiv2.indico.io/contentfiltering?key='17767cb46eb4b4f568832be2c953022b
should instead read:
http://apiv2.indico.io/contentfiltering?key=17767cb46eb4b4f568832be2c953022b
Since URL parameters are sometimes quoted and I'm not extremely familiar with objective-c's internal request handling I would guess that that is leading to the strange behavior you're seeing.
I'm new to Objective-C and am struggling to get the following code to work properly. Logging out dataString is telling me that the API is returning an "Authentication Required" message. When I put the resulting URL into a browser, however, the information I want is returned correctly. What am I missing? Is NSURLSession doing something to change the request?
- (void)fetchWX
{
NSString *requestString = [NSString stringWithFormat:#"http://%#:%##flightxml.flightaware.com/json/FlightXML2/Metar?airport=%#", FLIGHTAWARE_USERNAME, FLIGHTAWARE_API_KEY, _airport];
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.urlSession dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", dataString);
}];
[dataTask resume];
}
There's another method in my app with a similar structure that's working properly, and FlightAware's synchronous example using NSURLConnection also works fine. Just can't seem to use NSURLSession.
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
config.HTTPAdditionalHeaders = #{ #"Accept":#"application/json"};
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:path];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"jsonObject is %#",jsonObject);
}];
and add this delegate method, which will be called once to resolve the authentication challenge.
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {
NSString *user = #"YourUserName";
NSString *password = #"YourKey";
NSLog(#"didReceiveChallenge");
// should prompt for a password in a real app but we will hard code this baby
NSURLCredential *secretHandshake = [NSURLCredential credentialWithUser:user password:password persistence:NSURLCredentialPersistenceForSession];
// use block
completionHandler(NSURLSessionAuthChallengeUseCredential,secretHandshake);
}
I have tested it and it worked.
I'm attempting to communicate with a backend using NSURLSession, however, the backend wasn't set up to properly read NSData, so it views the NSData login details I send over as false, I wanna know if its possible to get NSURLSession to send raw strings rather than NSData objects. I've looked in books and the webs and I've been stumped for weeks.
Redoing the backend is not an option, the engineer in charge of that left. Any help is welcome.
Thanks. Here's what I've done so far, in case anyone needs to see some code.
NSError *error;
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration ];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSString *rawUrlString = #"backend_url";
NSURL *url = [NSURL URLWithString:rawUrlString];
NSLog(#"%#", url);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:
login data here
nil];
//NSLog(#"%#", parameters);
NSData *rawJson = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&error];
NSString *myString = [[NSString alloc] initWithData:rawJson encoding:NSUTF8StringEncoding];
NSData *finalData = [myString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *body = [NSMutableData data];
[body appendData:finalData];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:body];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"application/json" forHTTPHeaderField:#"charset"];
[request setValue:#"XMLHttpRequest" forHTTPHeaderField:#"X-Requested-With"];
NSURLSessionDataTask *postTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSLog(#"%#", [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]);
}];
[postTask resume];
`
UPDATE: I've cleaned up the unnecessary bits this is the final code (still doesn't work though)
NSError *error;
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration ];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSString *rawUrlString = #"backend_url";
NSURL *url = [NSURL URLWithString:rawUrlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:login details nil];
NSData *rawJson = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&error];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:rawJson];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSURLSessionDataTask *postTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSLog(#"%#", [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]);
}];
[postTask resume];
Your backend is setup to handle NSData. It's impossible to send a string over an internet connection, you can only send NSData and all servers expect it.
This code here is wrong:
NSData *rawJson = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&error];
NSString *myString = [[NSString alloc] initWithData:rawJson encoding:NSUTF8StringEncoding];
NSData *finalData = [myString dataUsingEncoding:NSUTF8StringEncoding];
Instead just do this:
NSData *finalData = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&error];
The NSData object will contain a string in the correct format for the server to also recognise as a string. Note it will be in UTF-8 encoding, perhaps on some servers you will want to change that to something else.