URLSession not working with URLCredential - ios

I have an API I am trying to connect to and the server is Windows Authentication.
I am trying to use URLSession with URLCredential with the delegate methods
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void){
var disposition: URLSession.AuthChallengeDisposition = URLSession.AuthChallengeDisposition.performDefaultHandling
var credential:URLCredential?
print(challenge.protectionSpace.authenticationMethod)
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
if (credential != nil) {
disposition = URLSession.AuthChallengeDisposition.useCredential
}
else
{
disposition = URLSession.AuthChallengeDisposition.performDefaultHandling
}
}
else
{
disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
}
completionHandler(disposition, credential);
}
This code runs twice as after doing some printing is because there are two Authentication Methods:
NSURLAuthenticationMethodServerTrust and NSURLAuthenticationMethodNTLM when it runs through the NSURLAuthenticationMethodServerTrust everything is fine, but when it runs NSURLAuthenticationMethodNTLM I get an error on this line:
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
saying this:
fatal error: unexpectedly found nil while unwrapping an Optional value
but only when I change this condition from
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
to
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM {
What Am I doing wrong?
Here is the method I am using to try to connect to that API
func loginUser(_ username: String, password: String, completion: #escaping (_ result: Bool) -> Void)
{
//Create request URL as String
let requestString = String(format:"%#", webservice) as String
//Covert URL request string to URL
guard let url = URL(string: requestString) else {
print("Error: cannot create URL")
return
}
//Convert URL to URLRequest
let urlRequest = URLRequest(url: url)
print(urlRequest)
//Add the username and password to URLCredential
credentials = URLCredential(user:username, password:password, persistence: .forSession)
print(credentials)
//Setup the URLSessionConfiguration
let config = URLSessionConfiguration.default
//Setup the URLSession
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
//Prepare the task to get data.
let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
DispatchQueue.main.async(execute: {
print(error!)
if(error == nil)
{
completion(true)
}
else
{
completion(false)
}
})
})
//Run the task to get data.
task.resume()
}

In the Apple documentation for URLProtectionSpace.serverTrust, it says:
nil if the authentication method of the protection space is not server trust.
So, you are trying to unwrap an optional value of nil, which of course would cause a crash.
In your case, you could probably just replace the entire function with (untested):
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void){
var disposition: URLSession.AuthChallengeDisposition = URLSession.AuthChallengeDisposition.performDefaultHandling
print(challenge.protectionSpace.authenticationMethod)
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
disposition = URLSession.AuthChallengeDisposition.performDefaultHandling
}
else
{
disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
}
completionHandler(disposition, credential);
}

Related

Swift 5 URLRequest authorization header: reserved, how to set?

In the documentation for Swift's URLRequest in Foundation, it says that the standard method of setting header values for a URLRequest shouldn't be used for reserved HTTP headers.
Following the link to the list of reserved HTTP headers a little bit deeper in the docs, it says that it may ignore attempts to set those headers.
But it also says that Authorization is a reserved HTTP header.
This can't be right, can it? A large percentage of the APIs in the universe require you to pass authentication tokens in a header of the form Authorization: Bearer {token}
So if Swift doesn't let you set the Authorization header, how does one access one of those APIs?
Following the documentation as you all mentioned, I've ended up for now to the following:
class ApiManager: NSObject {
var credential: URLCredential?
func token(withCredential credential: URLCredential?) {
guard let url = URL(string: "\(K.API)/token") else {
print("error URL: \(K.API)/token")
return
}
self.credential = credential
let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "accept")
request.setValue("application/json", forHTTPHeaderField: "content-type")
let task = session.dataTask(with: request) { (data, response, error) in
self.credential = nil
if error != nil {
print("URLSession error: \(error!.localizedDescription)")
return
}
guard let safeHttpResponse = response as? HTTPURLResponse else {
print("HTTPURLResponse error: \(error!.localizedDescription)")
return
}
if safeHttpResponse.statusCode == 200,
let safeData = data,
let dataString = String(data: safeData, encoding: .utf8) {
print("safeData: \(dataString)")
} else {
print("error: \(safeHttpResponse.statusCode)")
}
}
task.resume()
}
}
Here, token is a method as an example to authenticate a user.
I pass something like that from the UI to this method
URLCredential(user: usernameTextField.text, password: passwordTextField.text, persistence: .forSession)
Then the most important, is the URLSessionTaskDelegate
extension ApiManager: URLSessionTaskDelegate {
// From https://developer.apple.com/forums/thread/68809
// We should use session delegate as setting Authorization Header won't always work
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// This method is called mainly with HTTPS url
let protectionSpace = challenge.protectionSpace
let authMethod = protectionSpace.authenticationMethod
guard authMethod == NSURLAuthenticationMethodServerTrust, protectionSpace.host.contains(K.API.host) else {
completionHandler(.performDefaultHandling, nil)
return
}
guard let safeServerTrust = protectionSpace.serverTrust else {
completionHandler(.performDefaultHandling, nil)
return
}
DispatchQueue.global().async {
SecTrustEvaluateAsyncWithError(safeServerTrust, DispatchQueue.global()) { (trust, result, error) in
if result {
completionHandler(.useCredential, URLCredential(trust: trust))
} else {
print("Trust failed: \(error!.localizedDescription)")
completionHandler(.performDefaultHandling, nil)
}
}
}
}
func urlSession(
_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// This method is called for authentication
let protectionSpace = challenge.protectionSpace
let authMethod = protectionSpace.authenticationMethod
switch (authMethod, protectionSpace.host) {
case (NSURLAuthenticationMethodHTTPBasic, K.API.host):
self.basicAuth(didReceive: challenge, completionHandler: completionHandler)
// we could add other authentication e.g Digest
default:
completionHandler(.performDefaultHandling, nil)
}
}
private func basicAuth(
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.previousFailureCount < 3 {
completionHandler(.useCredential, self.credential)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
And I call everything like this:
let apiManager = ApiManager()
let credential = URLCredential(user: email, password: password, persistence: .forSession)
apiManager.token(withCredential: credential)
I have to handle the response with a completionHandler for example but the request is authenticated and works
Implement authentication challenge to handle Basic authentication like:
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
switch challenge.protectionSpace.authenticationMethod {
case NSURLAuthenticationMethodHTTPBasic:
performBasicAuthentication(challenge: challenge, completionHandler: completionHandler)
default:
completionHandler(.performDefaultHandling, nil)
}
}
func performBasicAuthentication(challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let credential = URLCredential(user: "username", password: "password", persistence: .forSession)
completionHandler(.useCredential, credential)
}
Here is the reference link
Setting or adding a value directly like other suggestions doesn't work in my case. I managed to solve it by creating additional HTTP Headers URLSessionConfiguration. Here's the code:
var sessionConfig = URLSessionConfiguration.default
var authValue: String? = "Bearer \(token!)"
sessionConfig.httpAdditionalHeaders = ["Authorization": authValue]
var session = URLSession(configuration: sessionConfig, delegate: self as? URLSessionDelegate, delegateQueue: nil)
session.dataTask(with: request) { data, response, error in
guard error == nil else { return }
guard let data = data else { return }
if let str = String(data: data, encoding: .utf8) {
}
}.resume()

Swift : TIC SSL Trust Error on loading self signed url in WebView

I was trying to load a self signed url inside Webview in a iOS app. Other urls are loading perfectly inside my webview.
I have added inside my info.plist file but getting error :-
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
And here is my full error :-
2019-01-08 12:26:52.386721+0530 Webview demo[761:27005] TIC SSL Trust
Error [2:0x282e90480]: 3:0
2019-01-08 12:26:52.405216+0530 Webview demo[761:27005]
NSURLSession/NSURLConnection HTTP load failed
(kCFStreamErrorDomainSSL, -9807)
2019-01-08 12:26:52.405283+0530 Webview demo[761:27005] Task
.<0> HTTP load failed (error
code: -1202 [3:-9807])
2019-01-08 12:26:52.405519+0530 Webview demo[761:27003]
NSURLConnection finished with error - code -1202
I am new to iOS please help me to understand and solve the problem.
Here is my ViewController :-
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://stackoverflow.com")
if let unwrappedUrl = url{
let request = URLRequest(url : unwrappedUrl)
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response , error) in
if error == nil {
DispatchQueue.main.async {
self.webView.loadRequest(request)
}
}
}
task.resume()
}
}
}
All you have to do is add some coding stuff instead of adding keys - value in Info.plist
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"your site": .disableEvaluation
]
Add above stuff in your session manager.
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
If you use alamofire them you can utilize the below method,
func urlSession(
_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
var credential: URLCredential?
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
(disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let host = challenge.protectionSpace.host
if
let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
let serverTrust = challenge.protectionSpace.serverTrust
{
if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
disposition = .useCredential
credential = URLCredential(trust: serverTrust)
} else {
disposition = .cancelAuthenticationChallenge
}
}
} else {
if challenge.previousFailureCount > 0 {
disposition = .rejectProtectionSpace
} else {
credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
if credential != nil {
disposition = .useCredential
}
}
}
completionHandler(disposition, credential)
}

Upload video to Vimeo using URLSession

Im trying to upload a video to Vimeo , using their rest API.
First I make a POST, to create the video on the API, then I select the video from camera, and send a PATCH with some parameters.
The problem is, the video seems to upload with success, I get a HTML without any errors that seems a success, but in the site doesn't show the video, and the KB size is only 0.
Here's my PATCH method :
let request = NSMutableURLRequest(url: "https://api.vimeo.com/me/videos")
request.httpMethod = "PATCH"
request.cachePolicy = .reloadIgnoringLocalCacheData
request.httpBody = mediaData // Data()
request.setValue("Upload-Offset", forHTTPHeaderField: "0")
request.setValue("Content-Type", forHTTPHeaderField: "application/offset+octet-stream")
request.setValue("Authorization", forHTTPHeaderField: "Bearer 1233131312")
request.setValue("Tus-Resumable", forHTTPHeaderField: "1.0.0")
let newTask = session.uploadTask(withStreamedRequest: request)
newTask.resume()
and already tried with:
request.httpBodyStream = InputStream(data: mediaData)
or:
session.uploadTask(with: request, from: data)
Heres is the documentation from VIMEO :
https://developer.vimeo.com/api/upload/videos
Can anyone have an example or snippet that actually works?
[UPDATE]:
The class that make the request:
import Foundation
case get
case post
case put
case delete
case patch
var verbName: String {
switch self {
case .get:
return "GET"
case .post:
return "POST"
case .put:
return "PUT"
case .delete:
return "DELETE"
case .patch:
return "PATCH"
}
}
var containsBody: Bool {
return self == .post || self == .put || self == .patch
}
}
final class MediaUploader: URLSessionTaskDelegate, URLSessionDataDelegate {
private let dispatchGroup = DispatchGroup()
private let globalSSLEnabled: Bool = true
var outputStream: OutputStream? = nil
private var buffer:NSMutableData = NSMutableData()
convenience init?(domain: String) {
self.init(domain: domain)
}
func request(verb: HTTPVerb, action: String, parameters: JSONDictionary = [:], headers: [String : String] = [:], sslEnabled: Bool = true, media: MediaData, completion: #escaping (_ response: HTTPResponse) -> Void) {
let sslEnabled = self.globalSSLEnabled || sslEnabled
guard let request = buildRequest(verb: verb, action: action, parameters: parameters, media: media, sslEnabled: sslEnabled) else {
return
}
headers.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
let session = buildURLSession(sslEnabled: sslEnabled)
conditionalLog(items: "REQUEST [\(verb.verbName)]")
conditionalLog(items: "URL: \(request.url?.absoluteString ?? "[invalid]")")
conditionalLog(items: "Headers: \(String(describing: request.allHTTPHeaderFields))")
conditionalLog(items: "Parameters: \(parameters)")
createSessionWithDataTask(session: session, request: request as URLRequest, mediaData: media, completion: completion)
}
func buildURLSession(sslEnabled: Bool) -> URLSession {
if !sslEnabled {
return URLSession(configuration: .default)
}
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
return session
}
func createSessionWithDataTask(session: URLSession, request: URLRequest, mediaData: MediaData,completion: #escaping (HTTPResponse) -> Void) {
let queue = DispatchQueue(label: "com.lassie.dispatchgroup", attributes: .concurrent, target: nil)
dispatchGroup.enter()
queue.async(group: dispatchGroup) {
self.dataTaskWorker(session: session, request: request as URLRequest, mediaData: mediaData, completion: completion)
self.dispatchGroup.leave()
}
}
func dataTaskWorker(session: URLSession, request: URLRequest, mediaData: MediaData, completion: #escaping (_ response: HTTPResponse) -> Void) {
guard let data = mediaData.data() else {
return
}
// let newTask = session.uploadTask(withStreamedRequest: request)
// let newTask = session.uploadTask(with: request, from: data)
//session.uploadTask(with: request, from: data)
let task = session.uploadTask(withStreamedRequest: request) { data, response, error in
self.conditionalLog(items: "RESPONSE: \(String(describing: response))")
if let eRROR = error {
self.conditionalLog(items: "ERROR : \(String(describing: eRROR))")
}
self.parseResponse(data: data, response: response, error: error, completion: completion)
session.finishTasksAndInvalidate()
}
newTask.resume()
}
func buildRequest(verb: HTTPVerb, action: String, parameters: JSONDictionary = [:], media: MediaData, sslEnabled: Bool) -> NSMutableURLRequest? {
let suffix = buildAction(verb: verb, action: action, parameters: parameters)
guard let fullUrl = suffix.isEmpty ? baseURL(sslEnabled: sslEnabled) : URL(string: suffix, relativeTo: baseURL(sslEnabled: sslEnabled)), let mediaData = media.data() else {
assert(false, "Invalid url/parameters")
return nil
}
let request = NSMutableURLRequest(url: fullUrl)
request.httpMethod = verb.verbName
request.cachePolicy = .reloadIgnoringLocalCacheData
request.httpBody = mediaData
// request.httpBodyStream = InputStream(data: mediaData)
return request
}
// MARK: URLSessionDataTask
func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: #escaping (InputStream?) -> Void) {
self.closeStream()
var inStream: InputStream? = nil
var outStream: OutputStream? = nil
Stream.getBoundStreams(withBufferSize: 4096, inputStream: &inStream, outputStream: &outStream)
self.outputStream = outStream
completionHandler(inStream)
}
private func closeStream() {
if let stream = self.outputStream {
stream.close()
self.outputStream = nil
}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
buffer.append(data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
let percentageSent = Double(totalBytesSent) / Double(totalBytesExpectedToSend)
print("PERCENTAGE - \(percentageSent)")
}
private func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
completionHandler(.allow)
}
private func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let e = error {
print("ERROR")
} else {
self.conditionalLog(items: "RESPONSE DATA: \(String(data: buffer as Data, encoding: .utf8))")
}
}
}
You are using session.uploadTask(withStreamedRequest: request). The reason the file size is 0 on backend is you didn't close the stream, something like inputStream.finished = true. So you have to figure out when you want to close that stream.
Anyways, since you already have the data ready. i would say you can use uploadTask(with:from:completionHandler:), or even switching to uploadTask(with:fromFile:completionHandler:)

Swift - NSURLSession for Windows Authentication

I have this class here and inside the class is a method and I am trying to do an NSURLSession on an API that requires windows authentication username and password. I have followed the tutorial here https://gist.github.com/n8armstrong/5c5c828f1b82b0315e24
and came up with this:
let webservice = "https://api.com"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let urlSession = NSURLSession(configuration: config)
class WebService: NSObject {
func loginUser(username: String, password: String) -> Bool {
let userPasswordString = "username#domain.com:Password"
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions([])
let authString = "Basic \(base64EncodedCredential)"
config.HTTPAdditionalHeaders = ["Authorization" : authString]
let requestString = NSString(format:"%#", webservice) as String
let url: NSURL! = NSURL(string: requestString)
let task = urlSession.dataTaskWithURL(url) {
(let data, let response, let error) in
if (response as? NSHTTPURLResponse) != nil {
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print(dataString)
}
}
task.resume()
return true
}
}
but when I run this I get a 401 error: 401 - Unauthorized: Access is denied due to invalid credentials.
I have confirmed the URL to the API is correct. Same with the username and password. What am I doing wrong?
I was able to fix this by doing the following:
var credential: NSURLCredential!
func loginUser(username: String, password: String) -> Bool {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
credential = NSURLCredential(user:username, password:password, persistence: .ForSession)
let requestString = NSString(format:"%#", webservice) as String
let url: NSURL! = NSURL(string: requestString)
let task = session.dataTaskWithURL(url, completionHandler: {
data, response, error in
dispatch_async(dispatch_get_main_queue(),
{
if(error == nil)
{
print("Yay!")
}
else
{
print("Naw!")
}
})
})
task.resume()
return true
}
and then adding NSURLSessionDelegate methods:
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if challenge.previousFailureCount > 0
{
completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil)
}
else
{
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust:challenge.protectionSpace.serverTrust!))
}
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential)
}

Swift 2 nil NSURLSessionDataTask when adding configuration

When session is set as .init(configuration:..., delegate:self, delegateQueue:NSOperationQueue.mainQueue()) for proxy server redirect, NSURLSessionDataTask.resume() does not result in execution of task. When session is set as .sharedSession(), task executes as expected.
**kCFStreamPropertyHTTPProxyHost etc. have been deprecated. Maybe this affects NSURLSessionConfiguration in a way that prevents execution of task?
class ConnectionManager: NSURLSession, NSURLSessionDelegate {
.
.
.
if shouldUseProxy {
let proxyEnable = NSNumber(int: 1) as CFNumber
let proxyDict: [NSObject:AnyObject] = [
kCFNetworkProxiesHTTPEnable: proxyEnable,
kCFStreamPropertyHTTPProxyHost: proxyHost,
kCFStreamPropertyHTTPProxyPort: proxyPort,
kCFStreamPropertyHTTPSProxyHost: proxyHost,
kCFStreamPropertyHTTPSProxyPort: proxyPort,
kCFProxyTypeKey: kCFProxyTypeHTTPS,
kCFProxyUsernameKey: proxyUser,
kCFProxyPasswordKey: proxyPW
]
let config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
config.connectionProxyDictionary = proxyDict
self.session = NSURLSession.init(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
} else {
self.session = NSURLSession.sharedSession()
}
self.task = self.session.dataTaskWithRequest(request) {
(data, response, error) in
if error == nil {
self.cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookiesForURL(response!.URL!)!
self.httpResponse = (response as? NSHTTPURLResponse)!
self.statusCode = (self.httpResponse!.statusCode)
guard error == nil && data != nil else {
print(error)
return
}
do {
if self.statusCode == 200 {
self.contentsOfURL = try NSString(contentsOfURL: self.URL, encoding: NSUTF8StringEncoding) as String
}
} catch {
}
}
}
self.task?.resume()
.
.
.
}
Setting the configuration
In most cases the default configuration works unless you need something special.
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfiguration,
delegate: self,
delegateQueue: operationQueue)
Pls refer Apple's documentation: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/#//apple_ref/doc/uid/TP40013435-CH1-SW7
Redirect response
NSURLSessionDelegate has the below mentioned function which gets called when a redirect happens.
func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: (NSURLRequest?) -> Void) {
//self.completionHandler is some completion handler you have defined, it is call back function.
if cancelled {
self.completionHandler(nil, redirectRequest:nil, wasCancelled: true)
}
else {
self.completionHandler(nil, redirectRequest:request, wasCancelled: false)
}
finished = true
executing = false
}
There is a parameter called request which contains the new URLRequest. Now you have to initiate a new session task with this new request
Challenge
This function is part of NSURLSessionDelegate
Whenever challenge is received the following function is called. Depending on the type of challenge, you have to handle it appropriately.
See the commented out code for NSURLAuthenticationMethodHTTPBasic and NSURLAuthenticationMethodHTTPDigest
func URLSession(session: NSURLSession,
didReceiveChallenge challenge: NSURLAuthenticationChallenge,
completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
print("Challenge:")
if(challenge.previousFailureCount == 0) {
let credential = credentialForChallenge(challenge)
print("Use Credential ....\n")
completionHandler(.UseCredential, credential)
}
else {
print("Previous Failure Count = \(challenge.previousFailureCount)")
print("Cancelling Challenge\n")
challenge.sender?.cancelAuthenticationChallenge(challenge)
}
}
//Custom method to get the credential for the challenge
//In your case you would be interested in NSURLAuthenticationMethodHTTPBasic
func credentialForChallenge(challenge : NSURLAuthenticationChallenge) -> NSURLCredential? {
//https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html
//challenge knows:
//what triggered the challenge
//how many attempts were made for the challenge - previousFailureCount
//any previous attempted credentials - proposedCredential
//the NSURLProtectionSpace that requires the credentials
//sender of the challenge
//#Respond
//Get authentication method
let credential : NSURLCredential?
print("Method = \(challenge.protectionSpace.authenticationMethod)")
switch(challenge.protectionSpace.authenticationMethod) {
case NSURLAuthenticationMethodHTTPBasic:
// HTTP basic authentication
// prompt user for username and password
// let credential = NSURLCredential(user: <#T##String#>, password: <#T##String#>, persistence: <#T##NSURLCredentialPersistence#>)
credential = nil
case NSURLAuthenticationMethodHTTPDigest:
// HTTP digest authentication
// prompt user for username and password
// let credential = NSURLCredential(user: <#T##String#>, password: <#T##String#>, persistence: <#T##NSURLCredentialPersistence#>)
credential = nil
case NSURLAuthenticationMethodClientCertificate:
// Client certificate authentication
// let credential = NSURLCredential(identity: <#T##SecIdentity#>, certificates: <#T##[AnyObject]?#>, persistence: <#T##NSURLCredentialPersistence#>)
credential = nil
case NSURLAuthenticationMethodServerTrust:
// Server trust authentication
if let serverTrustExists = challenge.protectionSpace.serverTrust {
credential = NSURLCredential(trust: serverTrustExists)
}
else {
credential = nil
}
default:
credential = nil
}
return credential
}

Resources