NSURLSession with Token Authentication - ios

I have the following code in my iOS project and I want to convert to use NSURLSession instead of NSURLConnection. I am querying a REST API which uses a token-based HTTP Authentication scheme but I cannot find an example of how to do it.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
NSString *username = [[NSUserDefaults standardUserDefaults] stringForKey:#"Username"];
NSString *token = //GET THE TOKEN FROM THE KEYCHAIN
NSString *authValue = [NSString stringWithFormat:#"Token %#",token];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
if ([NSURLConnection canHandleRequest:request]){
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[NSURLConnection sendAsynchronousRequest:request queue:self.fetchQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!connectionError) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == 200){
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:nil];
//Process the data
}
}
}];
}

You can rewrite it using NSURLSession as follows
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSString *token ; //GET THE TOKEN FROM THE KEYCHAIN
NSString *authValue = [NSString stringWithFormat:#"Token %#",token];
//Configure your session with common header fields like authorization etc
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = #{#"Authorization": authValue};
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSString *url;
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];

This is in Swift, but the logic is the same:
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let url = NSURL(string: "some url")
let request = NSMutableURLRequest(URL: url!)
request.setValue("value", forHTTPHeaderField: "header field")
let urlSession = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
let dataTask = urlSession.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
}
dataTask.resume()

Set the Authorization header on your URLSession configuration, or directly on your request.
Be advised that Apple says you "should not" attempt to modify the Authorization header in your URLSession configuration:
An URLSession object is designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers:
Authorization
...
It is, however, possible. If you want to do this, ensure that you set the header configuration before you create the URLSession. It's not possible to modify headers on an existing URLSession.
Here is a Swift playground that shows different scenarios:
import Foundation
// 1: Wrong -- mutating existing config for existing URLSession is no-op
var session = URLSession.shared
session.configuration.httpAdditionalHeaders = ["Authorization": "123"]
let url = URL(string: "https://postman-echo.com/get")!
session.dataTask(with: url) { (data, resp, err) in
if let data = data {
let str = String(bytes: data, encoding: .utf8)!
let _ = str
}
}.resume() // no authorization header
// 2: Setting headers on an individual request works fine.
var request = URLRequest(url: url)
request.addValue("456", forHTTPHeaderField: "Authorization")
session.dataTask(with: request) { (data, resp, err) in
if let data = data {
let str = String(bytes: data, encoding: .utf8)!
let _ = str
}
}.resume() // "headers": { "authorization": "456" }
// 3: You can set headers on a new URLSession & configuration
var conf = URLSessionConfiguration.default
conf.httpAdditionalHeaders = [
"Authorization": "789"
]
session = URLSession(configuration: conf)
session.dataTask(with: url) { (data, resp, err) in
if let data = data {
let str = String(bytes: data, encoding: .utf8)!
let _ = str
}
}.resume() // "headers": { "authorization": "789" }

Related

How to post raw data in swift 3?

If I post raw data using Postman, response is coming. I am using this code
var dict = Dictionary<String, Any>()
dict = ["user_id" :userid as AnyObject, "type" :type as AnyObject, "complaint_id" :complaintId as AnyObject, "auth_code" : authCode as AnyObject, "isSkip" :isSkip as AnyObject]
let url:URL = URL(string: "http://development.easystartup.org/prigovo/Backend/detailed_complaint/index.php")!
let session = URLSession.shared
var postData = NSData()
do{
postData = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions.prettyPrinted) as NSData!
}catch {
print("error")
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
// request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.setValue("\(postData.length)", forHTTPHeaderField: "Content-Length")
request.setValue("text/html", forHTTPHeaderField: "Content-Type")
request.setValue("json/application", forHTTPHeaderField: "Accept")
request.httpBody = postData as Data
let task = session.dataTask(with: request as URLRequest) {
(
data, response, error) in
guard let data = data, let _:URLResponse = response, error == nil else {
print("error")
return
}
let dataString = String(data: data, encoding: String.Encoding.utf8)
print(dataString ?? "no data")
}
task.resume()
Getting data of 0 bytes everyTime. Already tried with Alamofire but no response.
Also I tried in Objective C where I am getting response, code is :
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"text/html" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSError *error = nil;
NSHTTPURLResponse *response = nil;
[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSData *retData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error)
{
//error
NSLog(#"error");
return #"";
}
else
{
NSLog(#"No error");
NSString *charlieSendString = [[NSString alloc] initWithData:retData encoding:NSUTF8StringEncoding];
NSLog(#"data come : %#",charlieSendString);
return charlieSendString;
}
Posted "dict" in Log :
["complaint_id": COMBRD1, "user_id": USR9, "type": complaint_brand, "auth_code": KL1hwYrAhNVnSgT, "is_skip": 2]
var dict = Dictionary<String, Any>()
dict = ["user_id" :userid, "type" :type, "complaint_id" :complaintId,"auth_code" : authCode, "is_skip" :isSkip]
var jsonData = NSData()
// var dataString2 :String = ""
do {
jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) as NSData
// you can now cast it with the right type
} catch {
print(error.localizedDescription)
}
let url:URL = URL(string: "http://Backend/detailed_complaint/index.php")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("\(jsonData.length)", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData as Data
let task = session.dataTask(with: request as URLRequest) {
(
data, response, error) in
guard let data = data, let _:URLResponse = response, error == nil else {
print("error")
return
}
let dataString = String(data: data, encoding: String.Encoding.utf8)
print("no data",dataString ?? "no data")
}
task.resume()

Basic Authentication in Swift 3 does't work

I am struggling with basic authentication in Swift.
I have a Rest back end service over SSL and with basic authentication. My objective-c client code works well but the corresponding Swift one doesn't work because the authentication fails.
This is the Swift code:
let sUrl = "HTTPS://localhost:8443/Test_1/rest/Service/returnInfo"
let url: URL = URL(string: sUrl)!
let request: URLRequest = URLRequest(url: url);
let session: URLSession = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue())
let task: URLSessionDataTask = session.dataTask(with: request) { (data, response, inError) in {
...
let httpResponse = response as! HTTPURLResponse
if (httpResponse.statusCode != 200) {
let details = [NSLocalizedDescriptionKey: "HTTP Error"]
let error = NSError(domain:"WS", code:httpResponse.statusCode, userInfo:details)
completionHandler(nil, error);
return
}
...
}
task.resume()
The delegate method is quite similar to the corresponding method in Objective-c:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.previousFailureCount == 0 else {
challenge.sender?.cancel(challenge)
// Inform the user that the user name and password are incorrect
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let proposedCredential = URLCredential(user: user!, password: password!, persistence: .none)
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, proposedCredential)
}
The httpResponse.statusCode is always 401.
The delegate method is called only once, instead the corresponding method in Objective-c is called two times.
Where am I wrong?
UPDATE
The corresponding Objective-c code:
NSString *sUrl = [NSString stringWithFormat:#"HTTPS://localhost:8443/Test_1/rest/Service/returnInfo"];
NSURL *url = [NSURL URLWithString:sUrl];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *inError) {
if (inError != nil) {
completionHandler(0, inError);
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode != 200) {
NSDictionary *details = #{NSLocalizedDescriptionKey:#"HTTP Error"};
NSError *error = [NSError errorWithDomain:#"WS" code:httpResponse.statusCode userInfo:details];
completionHandler(0, error);
return;
}
NSError *jsonError;
NSDictionary *valueAsDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
if (jsonError != nil) {
completionHandler(0, jsonError);
return;
}
if (![valueAsDictionary[#"ret"] boolValue]) {
NSInteger code = [valueAsDictionary[#"code"] integerValue];
NSDictionary *details = #{NSLocalizedDescriptionKey:(valueAsDictionary[#"message"]!=nil) ? valueAsDictionary[#"message"] : #""};
NSError *error = [NSError errorWithDomain:#"WS" code:code userInfo:details];
completionHandler(0, error);
return;
}
completionHandler(valueAsDictionary[#"value"], nil);
}];
[task resume];
This is the delegate function:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:_user password:_password persistence:NSURLCredentialPersistenceNone];
completionHandler(NSURLSessionAuthChallengeUseCredential, newCredential);
} else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
I eventually managed to make it working in Swift, even if I don't know because it was not working before.
Apparently, user and password have to be explicitly added to the HTTP headers.
let sUrl = "HTTPS://localhost:8443/Test_1/rest/Service/returnInfo"
let url: URL = URL(string: sUrl)!
let request: URLRequest = URLRequest(url: url);
// Changes from here ...
let config = URLSessionConfiguration.default
let userPasswordData = "\(user!):\(password!)".data(using: .utf8)
let base64EncodedCredential = userPasswordData!.base64EncodedString(options: Data.Base64EncodingOptions.init(rawValue: 0))
let authString = "Basic \(base64EncodedCredential)"
config.httpAdditionalHeaders = ["Authorization" : authString]
let session: URLSession = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
// ... to here
let task: URLSessionDataTask = session.dataTask(with: request) { (data, response, inError) in {
...
let httpResponse = response as! HTTPURLResponse
if (httpResponse.statusCode != 200) {
let details = [NSLocalizedDescriptionKey: "HTTP Error"]
let error = NSError(domain:"WS", code:httpResponse.statusCode, userInfo:details)
completionHandler(nil, error);
return
}
...
}
task.resume()
This code is worked for me in Swift 3.0.1:
let login = "username"
let password = "password"
let sUrl = NSURL(string: (urlString as NSString) as String)
let request: URLRequest = URLRequest(url: sUrl as! URL);
let config = URLSessionConfiguration.default
let userPasswordData = "\(login):\(password)".data(using: .utf8)
let base64EncodedCredential = userPasswordData!.base64EncodedString(options: Data.Base64EncodingOptions.init(rawValue: 0))
let authString = "Basic \(base64EncodedCredential)"
config.httpAdditionalHeaders = ["Authorization" : authString]
let session: URLSession = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
let task = session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
print("response \(data)")
let httpResponse = response as! HTTPURLResponse
if (httpResponse.statusCode != 200) {
print(error?.localizedDescription as Any)
print("Handle Error")
}
else{
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
print("Synchronous\(jsonResult)")
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
task.resume()
}
According to your question, this is your request (line) instance.
let request: URLRequest = URLRequest(url: url);
You have not set any header parameters for your request instance, here. Please compare request header and body parameters with you objective C client.
Header params may include - content type as well as other useful confidential param like API keys also.
Check your objective C client request and set same params here in your swift code

How to send JSON data using POST method swift

How to send JSON data using POST method to server in SWIFT
For Objective C i use this
NSMutableDictionary *get = [[NSMutableDictionary alloc]init];
[get setObject:validEmailTF.text forKey:#"email"];
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:get options:kNilOptions error:nil];
NSString *jsonInputString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *post = [[NSString alloc]initWithFormat:#"r=%#",jsonInputString];
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"%#",forgetPasswordUrl]];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:120.0];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSError *error;
NSURLResponse *response;
NSData *responseData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (responseData != nil)
{
jsonDict = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSLog(#"Values =======%#",jsonDict);
}
Convert this code in Swift language.
Thanks
You can try this, may be it will help you
let request = NSMutableURLRequest(URL: NSURL(string: "Your forgetPasswordUrl")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let params = ["email":validEmailTF.text] as Dictionary<String, String>
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")})
task.resume()
Simply converted your code to swift without tuning your code.
Try this:
var get: NSMutableDictionary = NSMutableDictionary()
get["email"] = validEmailTF.text
var jsonData: NSData = NSJSONSerialization.dataWithJSONObject(get, options: kNilOptions, error: nil)
var jsonInputString: String = NSString(data: jsonData, encoding: NSUTF8StringEncoding)
var post: String = NSString(format: "r=%#",jsonInputString)
var get: NSMutableDictionary = NSMutableDictionary()
var url: NSURL = NSURL(string: "\(forgetPasswordUrl)")
var postData: NSData = post.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)
var postLength: String = "\(postData.length())"
var request: NSMutableURLRequest = NSMutableURLRequest.requestWithURL(url, cachePolicy: NSURLRequestReloadIgnoringCacheData, timeoutInterval: 120.0)
request.URL = url
request.HTTPMethod = "POST"
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPBody = postData
var error: NSError
var response: NSURLResponse
var responseData: NSData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
if responseData != nil {
jsonDict = NSJSONSerialization.JSONObjectWithData(responseData, options: kNilOptions, error: &error)
print("Values =======\(jsonDict)")

Can't get allHeaderFields from NSURLConnection.sendSynchronousRequest with swift

As the response required for NSURLConnection.sendSynchronousRequest is now requiring NSURLResponse not NSHTTPURLResponse I can't get allHeaderFields.. Is there something I can do here?
var newRequest: NSMutableURLRequest = NSMutableURLRequest(URL: request.URL)
newRequest.HTTPMethod = "HEAD"
var response: NSURLResponse
NSURLConnection.sendSynchronousRequest(newRequest, returningResponse: &response, error: nil)
if response.respondsToSelector(Selector(allHeaderFields)) {
let allHeaders = response.allHeaderFields
}
In the old Objective-C version I was doing this which no longer works in swift..
NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:[request URL]];
[newRequest setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error: NULL];
if ([response respondsToSelector:#selector(allHeaderFields)]) {
NSDictionary *dictionary = [response allHeaderFields];
}
Rather than respondsToSelector, you should use optional binding, casting it to a NSHTTPURLResponse:
let newRequest: NSMutableURLRequest = NSMutableURLRequest(URL: url)
newRequest.HTTPMethod = "HEAD"
var response: NSURLResponse?
NSURLConnection.sendSynchronousRequest(newRequest, returningResponse: &response, error: nil)
if let httpResponse = response as? NSHTTPURLResponse {
// use `httpResponse.allHeaderFields`
}

URL File Size With NSURLConnection - Swift

i am trying to get a file size from url before downloading
here is the obj-c code
NSURL *URL = [NSURL URLWithString:"ExampleURL"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: nil];
long long size = [response expectedContentLength];
and here is Swift Code
var url:NSURL = NSURL(string: "ExmapleURL")
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "HEAD"
var response = NSHTTPURLResponse()
NSURLConnection.sendSynchronousRequest(request, returningResponse: &response , error: nil)
but i have error here
NSURLConnection.sendSynchronousRequest(request, returningResponse: &response , error: nil)
'NSHTTPURLResponse' is not identical to 'NSURLResponse?'
did i miss something in swift here ?
The response parameter has the type
AutoreleasingUnsafeMutablePointer<NSURLResponse?>
which means that you can pass the address of an optional NSURLResponse as argument:
var response : NSURLResponse?
NSURLConnection.sendSynchronousRequest(request, returningResponse: &response , error: nil)
You can then conditionally cast the returned response to a NSHTTPURLResponse:
if let httpResponse = response as? NSHTTPURLResponse {
println(httpResponse.expectedContentLength)
}
Note that you should check the return value of sendSynchronousRequest(), which
is nil if no connection could be made.
It is also recommended to call this
method only from a separate thread (or use sendAsynchronousRequest() instead)
because it can take a while to make a connection
– in particular when using a cellular network – and the main thread would be
blocked otherwise.
Swift 4 solution:
func fetchContentLength(for url: URL, completionHandler: #escaping (_ contentLength: Int64?) -> ()) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil, let response = response as? HTTPURLResponse, let contentLength = response.allHeaderFields["Content-Length"] as? String else {
completionHandler(nil)
return
}
completionHandler(Int64(contentLength))
}
task.resume()
}
// Usage:
let url = URL(string: "https://s3.amazonaws.com/x265.org/video/Tears_400_x265.mp4")!
fetchContentLength(for: url, completionHandler: { contentLength in
print(contentLength ?? 0)
})

Resources