"Authentication Required" with NSURLSession. User/Key correct - ios

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.

Related

NSURLSession send token back to server not working with ios10

I am trying to use NSURLSession, I had successfully received token from server, now I have to used that Token to get further details...
I had tried below code to get user data..
but I am getting httpResponse code: 500
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSString *authValue = [arrTokenData valueForKey:#"Token"];
//Configure session with common header fields
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = #{#"bearer": authValue};
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSString *url = #"http://test.myserver.am/api/mobile/LookUps/getuserdata";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (!error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == 200)
{
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:nil];
//Process the data
}
}
}];
[task resume];
I am using iOS 10 , above code is not working, also I have tried several ways but no any success...
I have tried stackoverflow solution but this is also not working out for me...
Please help me out...

Trying to understand asynchronous calls

I have this method
- (NSString*) createUserWithName:(NSString*)TheName
{
NSURL *URL =someUrlthatIncludesTheName
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURL *URL = [NSURL URLWithString:url];
NSURLSessionTask *task = [session dataTaskWithURL:URL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (response) {
NSError* error = nil;
NSArray *output = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
myID = [[output objectAtIndex:0] objectForKey:#"UserID"];
}
}];
[task resume];
return myID;
}
and another method
-(void)doSomethingWith: (NSString*) anID
Somewhere in my code, I call these methods subsequently, like this:
[self createUserWithName:#"John"];
[self doSomethingWith:myID];
However, due to the fact that the NSURLSession in createUserWithName: is asynchronous, doSomethingWith: is fired with myID = (null).
What is the best way to approach this problem, without necessarily falling back to deprecated synchronous NSURLConnection?
Thanks in advance
The workflow is supposed to be
- (void)createUserWithName:(NSString*)TheName
{
NSURL *URL =someUrlthatIncludesTheName
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURL *URL = [NSURL URLWithString:url];
NSURLSessionTask *task = [session dataTaskWithURL:URL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (response) {
NSError* error = nil;
NSArray *output = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
myID = [[output objectAtIndex:0] objectForKey:#"UserID"];
[self doSomethingWith:myID];
}
}];
[task resume];
}
And the call is just
[self createUserWithName:#"John"];
The method doSomethingWith: is asynchronously executed in the completion block.
Alternatively use a custom completion block
- (void)createUserWithName:(NSString *)theName completion:^(NSString *identifier)completion
{
NSURL *URL =someUrlthatIncludesTheName
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURL *URL = [NSURL URLWithString:url];
NSURLSessionTask *task = [session dataTaskWithURL:URL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (response) {
NSError* error = nil;
NSArray *output = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
myID = [[output objectAtIndex:0] objectForKey:#"UserID"];
completion(myID);
}
}];
[task resume];
}
and call it with
[self createUserWithName:#"John" completion:^(NSString *identifier) {
[self doSomethingWith:identifier];
}];

Migrating to NSURLSession from NSURLConnection incompatible pointer types

I'm moving from NSURLConnection to NSURLSession and running into a problem with the NSMutableURLRequest that holds values for the URL request.
Any ideas on how to retain this info in the NSURLSession without getting this error:
incompatible pointer types sending nsmutableurlrequest to parameter of
type nesting * _nonull nsurl urlwithstring
I see that this line [NSURL URLWithString:myRequest] wants a NSString, but how to I still pass the other info that is on my NSMutableURLRequest?
NSMutableURLRequest *myRequest = [NSMutableURLRequest requestWithURL: myURL];
[acquisitionRequest setValue:userAgent forHTTPHeaderField:#"User-Agent"];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:myRequest]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
}] resume];
You should use dataTaskWithRequest:completionHandler: instead of dataTaskWithURL:completionHandler:. This way you can pass the whole request in. If it complains about being mutable, you can copy it to get rid of the mutable state.
NSMutableURLRequest *myRequest = [NSMutableURLRequest requestWithURL: myURL];
[acquisitionRequest setValue:userAgent forHTTPHeaderField:#"User-Agent"];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:[myRequest copy]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// completion stuff
}] resume];
Your problem is that you are passing NSMutableURLRequest object while creating URL using URLWithString: which is expecting you to pass NSString. Pass myURL instead of [NSURL URLWithString:myRequest] and you should be good.
However, I would also advise to use dataTaskWithRequest:completionHandler: instead.
I am receiving the similar issue. I can't login on iOS 9 because NSData has been deprecated. I need to update to NSURLSession. I am attempting to do so with the code below, but - It appears there are two lines still drawing from NSData, where as my pointer to them is an NSURLSession.
It is aggravating the system. How do I set the receiving lines open to connect with NSURLSession ?
NSURLSession *session=[NSURLSession sharedSession];
NSLog(#"response%#",response);
NSLog(#"Response code: %ld", (long)[response statusCode]);
if ([response statusCode] >= 200 && [response statusCode] < 300)
{
NSString *responseData = [[NSString alloc]initWithData:session encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
NSError *error = nil;
/* NSDictionary *jsonData = [NSJSONSerialization
JSONObjectWithData:urlData
options:NSJSONReadingMutableContainers
error:&error];*/
// NSLog(#"dict%#",jsonData);
self.topLevelArray3 = [NSJSONSerialization JSONObjectWithData:session options:NSJSONReadingMutableContainers error:&error];
for (int i=0; i<[self.topLevelArray3 count]; i++) {

Objective-C issues with dataTaskWithRequest

I have recently switched from sendSynchronousRequest to dataTaskWithRequest
with sendSynchronousRequest my method was working perfectly but when I switch to dataTaskWithRequest I get the following error:
error NSURLError * domain: #"NSURLErrorDomain" - code: 4294966096 0x15ee96c0
and
myError NSError * domain: nil - code: 1684370017 0x26cce125
I don't understand why.
Here is the old code (commented out) and the new code:
/*-(NSDictionary *)GetProductionScheduleData:(NSString *)areaDescription
{
NSString *areaDescriptionWSpaceCharacters = [areaDescription stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSString *requestString = [NSString stringWithFormat:#"%#?areaDescription=%#",kIP,areaDescriptionWSpaceCharacters];
NSURL *JSONURL = [NSURL URLWithString:requestString];
NSURLResponse* response = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
if(data == nil)
return nil;
NSError *myError;
NSDictionary *productionSchedule = [[NSDictionary alloc]initWithDictionary:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
return productionSchedule;
}*/
-(void)GetProductionScheduleData:(NSString *)areaDescription Completetion:(void (^) (NSMutableDictionary * result,NSError * error))completion{
NSString *areaDescriptionWSpaceCharacters = [areaDescription stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSString *requestString = [NSString stringWithFormat:#"%#?areaDescription=%#",kIP,areaDescriptionWSpaceCharacters];
NSURL *JSONURL = [NSURL URLWithString:requestString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSError *myError;
NSMutableDictionary *productionSchedule = [[NSMutableDictionary alloc]initWithDictionary:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
completion(productionSchedule,myError);
}];
[dataTask resume];
}
Please Help! This was working with sendSynchronousRequest I am starting to dislike dataTaskWithRequest.
The NSURLSession code you have is correct, I confirmed with a valid URL.
You stopped checking to see if data is nil before attempting to JSON parse it. If you add that check back I bet you'll find that there is an error and data is in fact nil.
Change to:
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// handle request error
if (error) {
completion(nil, error);
return;
}
NSError *myError;
NSMutableDictionary *productionSchedule = [[NSMutableDictionary alloc]initWithDictionary:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
completion(productionSchedule,myError);
}];
I would recommend also checking myError before attempting to set it to productionSchedule (which could also cause a crash).

Authentication Error with GitHub API on iOS

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

Resources