I'm a bit new to developing iOS applications, and one of the things I need to do in this app is connected to a REST Web Service. I have tested the WS in the Postman Extension and everything works fine, but I can't seem to do the same through the iOS application. This is the code that I use to try and establish the connection:
NSString *wsAuth = [NSString stringWithFormat:@"Basic \%@", [[@"USER:PASS" dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:nil]];
NSMutableDictionary *post = [[NSMutableDictionary alloc]init];
[post setValue:login forKey:@"user"];
[post setValue:pass forKey:@"pass"];
NSArray *arr = [NSArray arrayWithObjects:post, nil];
NSData *jsonPost = [NSJSONSerialization dataWithJSONObject:arr options:0 error:nil];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://mywebservice.mywebsite.com/auth/"]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:jsonPost];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%d", [jsonPost length]] forHTTPHeaderField:@"Content-Length"];
[request setValue:wsAuth forHTTPHeaderField:@"Authorization"];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conn start];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if([response statusCode] >=200 && [response statusCode] <300) {
// Process data...
}
else {
// Present error...
}
I received the following errors from Xcode:
2016-03-16 11:02:34.658 [31742:2969162] Response = (null)
2016-03-16 11:02:34.659 [31742:2969162] Error =
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."
UserInfo={NSErrorFailingURLStringKey=https://mywebservice.mywebsite.com/auth/,
_kCFStreamErrorCodeKey=-2102,
NSErrorFailingURLKey=https://mywebservice.mywebsite.com/auth/,
NSLocalizedDescription=The request timed out.,
_kCFStreamErrorDomainKey=4,
NSUnderlyingError=0x7aa5b510 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "The request timed out."
UserInfo={_kCFStreamErrorCodeKey=-2102,
NSErrorFailingURLStringKey=https://mywebservice.mywebsite.com/auth/,
NSErrorFailingURLKey=https://mywebservice.mywebsite.com/auth/,
NSLocalizedDescription=The request timed out.,
_kCFStreamErrorDomainKey=4}}}
2016-03-16 11:02:34.693 [31742:2969383] CFNetwork SSLHandshake failed (-9806)
2016-03-16 11:02:34.799 [31742:2969383] CFNetwork SSLHandshake failed (-9802)
2016-03-16 11:02:34.889 [31742:2969383] CFNetwork SSLHandshake failed (-9802)
2016-03-16 11:02:34.890 [31742:2969383] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
A couple extra things to know. I am programming this as an iOS 9 application that needs to work with iOS 8 or newer, and I am doing this in Xcode 7. Any help would be appreciated.
Okay so I figured this out on my own. I guess making a synchronous request did not work, so I switched to asynchronous. This gave me a more accurate error which showed me that instead of passing a JSON object, I was passing an array with the first and only entry was a JSON object, which was causing the authentication to fail.
Just to clarify, this solution contains several objects to communicate with the RESTful web service. This is done through a POST, using a JSON object as the POST variables. This web service requires Basic Authentication, which is achieved through this code at Lines 1 and 15. Finally, this is all based on a web service that is behind a secure connection and an up-to-date SSL certificate. I'm not 100% sure what the work around would be for an HTTP request, or a self-signed certificate...
Side note: I removed the NSAllowsArbitraryLoads
key and the NSAppTransportSecurity
dictionary from my info.plist
file and this solution still worked.
Here is the code that provided me with a successful connection to my secure Web Service:
NSString *wsAuth = [NSString stringWithFormat:@"Basic \%@", [[@"USER:PASS" dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]];
NSMutableDictionary *post = [[NSMutableDictionary alloc]init];
[post setValue:login forKey:@"user"];
[post setValue:pass forKey:@"pass"];
NSArray *arr = [NSArray arrayWithObjects:post, nil];
// Note the first value of the array is being referenced, not the entire array.
NSData *jsonPost = [NSJSONSerialization dataWithJSONObject:arr[0] options:0 error:nil];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://mywebservice.mywebsite.com/auth/"]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:jsonPost];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%d", [jsonPost length]] forHTTPHeaderField:@"Content-Length"];
[request setValue:wsAuth forHTTPHeaderField:@"Authorization"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ([data length] >0 && error == nil) {
// Process data here...
}
else if ([data length] == 0 && error == nil) {
NSLog(@"Empty Response. Check your credentials.");
}
else if (error != nil) {
NSLog(@"Error = %@", error);
}
}];