I'm developing an app that relies on a web service backend. I have a separate class API to handle all network activities and send the data to the relevant view controllers.
I have a NSURLSession.dataTaskWithRequest() which sends my data via POST to my API and returns a response. I figured it would be best to have a single variable networkResponse in my API class with a getter and setter as I'll only ever need one response at a time.
In my ViewController, I have a login form which makes a request to the API and will log the user in depending on the response. If the credentials are incorrect, it returns the response in a UIAlertController.
On testing incorrect credentials, I've found that the response is not getting called in time for the AlertController, so it just shows a blank message. If I dismiss it and click login again straight away, It shows the response. How would I go about getting it to display sooner?
login function:
class func login (username: String, password: String) {
config.HTTPAdditionalHeaders = ["Authorization" : authorization]
let url : NSURL = NSURL(string: "\(root)PHP/login.php")!
let request = NSMutableURLRequest(URL: url)
var returnString : NSString = ""
request.HTTPMethod = "POST"
let postString = "username=\(username)&password=\(password)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
var networkTask = mySession.dataTaskWithRequest(request) {
data, response, error in
var err : NSError?
dispatch_async(dispatch_get_main_queue()) {
/* Setting response variable here */
self.setNetworkResponse(NSString(data: data, encoding: NSUTF8StringEncoding)!)
}
}
networkTask.resume()
}
calling API:
#IBAction func login(sender: AnyObject) {
if !txtUsername.text.isEmpty || !txtPassword.text.isEmpty {
API.login(txtUsername.text, password: txtPassword.text)
Alert:
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
if identifier == "segueLogin" {
if API.getNetworkResponse() !== "true" {
var alert = UIAlertController(title: "Message", message: API.getNetworkResponse() as String, preferredStyle: .Alert)
While its probably not the most ideal solution, I've managed to fix the problem by setting a delay, using dispatch_after in the shouldPerformSegueWithIdentifier to delay the alert by about 5 seconds. The message appears first time now which is what I'm looking for.
Related
I am uploading png Images->Base64->jsonData in URLRequest.httpBody size around 6MB. I am using a static NSURLSession with default Configuration and uploading using dataTask on urlsession. Sometimes its successfully uploaded to server sometimes its not and getting below error and nothing is printing at server side. I am not making parallel calls. We are using SSL pinning and handling authentication challenges proper so no SSL authentication error.
iOS device 11.3 and XCode 10 we are using.
Task <58BF437E-7388-4AE4-B676-2485A57CB0CD>.<10> finished with error - code: -999
private lazy var configuration: URLSessionConfiguration = {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = TimeInterval(120)
configuration.timeoutIntervalForResource = TimeInterval(120)
return configuration
}()
private lazy var urlSession = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
func invokeService(methodName : String, collectionName: String, queryDictionary: [String:AnyObject]! = nil, httpMethod: String! = nil) {
// Set up the URL request
let baseUrl: String = WebServiceConstants.hostUrl.qaUrl + "/\(collectionName)" + "/\(methodName)"
// let baseUrl: String = WebServiceConstants.hostUrl.demoUrl + "/\(collectionName)" + "/\(methodName)"
// let baseUrl: String = WebServiceConstants.hostUrl.prod_Url + "/\(collectionName)" + "/\(methodName)"
guard let url = URL(string: baseUrl) else {
return
}
var urlRequest = URLRequest(url: url)
// set up the session
// let configuration = URLSessionConfiguration.default
// configuration.timeoutIntervalForRequest = TimeInterval(120)
// configuration.timeoutIntervalForResource = TimeInterval(120)
//
// let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
urlRequest.httpMethod = httpMethod
do {
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: queryDictionary, options: []) // pass dictionary to nsdata object and set it as request body
}
catch _ {
}
urlRequest.setValue(WebServiceConstants.HTTPStrings.contentTypeJSON, forHTTPHeaderField: WebServiceConstants.HTTPStrings.contentTypeHeader)
// print(queryDictionary)
if AppController.sharedInstance.isAlreadyLogin() && (KeychainWrapper.standard.string(forKey: Constants.UserDefaultKeys.authorizationHeaderValue) != nil) {
let authorizationHeaderValue = WebServiceConstants.HTTPStrings.authorizationHeaderValue + KeychainWrapper.standard.string(forKey: Constants.UserDefaultKeys.authorizationHeaderValue)!
urlRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: WebServiceConstants.HTTPStrings.authorizationHeader)
}
let _ = urlSession.dataTask(with: urlRequest, completionHandler: { [unowned self]
(data, response, error) in
//print(response)
if error != nil {
if error?._code == NSURLErrorTimedOut {
// print(error?.localizedDescription)
let userInfo = [
NSLocalizedDescriptionKey: BWLocalizer.sharedInstance.localizedStringForKey(key:"App_Timeout_Message")
]
let errorTemp = NSError(domain:"", code:-1001, userInfo:userInfo)
self.delegate?.didFailWithError(errorObject: errorTemp)
} else if error?._code == NSURLErrorNotConnectedToInternet {
let userInfo = [
NSLocalizedDescriptionKey: BWLocalizer.sharedInstance.localizedStringForKey(key:"Internet_Not_Available")
]
let errorTemp = NSError(domain:"", code:-1001, userInfo:userInfo)
self.delegate?.didFailWithError(errorObject: errorTemp)
}
else if error?._code == NSURLErrorCancelled {
// canceled
print("Request is cancelled") // Control reaches here on Finished with Error code = -999
self.delegate?.didFailWithError(errorObject: error!)
}
else {
self.delegate?.didFailWithError(errorObject: error!)
}
} else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
{
self.delegate?.didReceiveResponse(responseObject: json as AnyObject)
//Implement your logic
print(json)
}
} catch {
self.delegate?.didFailWithError(errorObject: error)
}
}
}).resume()
}
*Added SSL Certificate pinning code *
extension WebserviceHandler : URLSessionDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let trust = challenge.protectionSpace.serverTrust else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let credential = URLCredential(trust: trust)
let pinner = setupCertificatePinner() // adding CertificateHash
if (!pinner.validateCertificateTrustChain(trust)) {
challenge.sender?.cancel(challenge)
}
if (pinner.validateTrustPublicKeys(trust)) {
completionHandler(.useCredential, credential)
}
else {
completionHandler(.cancelAuthenticationChallenge, nil)
let popUp = UIAlertController(title: "", message: BWLocalizer.sharedInstance.localizedStringForKey(key:"Certificate_Pining_Fail_Message"), preferredStyle: UIAlertController.Style.alert)
popUp.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: {alertAction in popUp.dismiss(animated: true, completion: nil)}))
popUp.addAction(UIAlertAction(title: BWLocalizer.sharedInstance.localizedStringForKey(key:"Action_Go"), style: UIAlertAction.Style.default, handler: { action in
let urlToOpenAppStorepage = URL(string: WebServiceConstants.hostUrl.appstore_url)
let objApp = UIApplication.shared
objApp.openURL(urlToOpenAppStorepage!)
}))
UIApplication.shared.keyWindow?.rootViewController?.present(popUp, animated: true, completion: nil)
}
}
}
func setupCertificatePinner() -> CertificatePinner {
// let baseUrl: String = WebServiceConstants.hostUrl.dev_URL
let baseUrl: String = WebServiceConstants.hostUrl.qaUrl
// let baseUrl: String = WebServiceConstants.hostUrl.demoUrl
// let baseUrl: String = WebServiceConstants.hostUrl.prod_Url
let pinner = CertificatePinner(baseUrl)
/*
You will see something like this:
being challanged! for www.google.co.nz
hash order is usually most specific to least, so the first one is your domain, the last is the root CA
Production
hash: 8U/k3RvTcMVSafJeS9NGpY4KDFdTLwpQ/GUc+lmPH/s=
hash: 4vkhpuZeIkPQ+6k0lXGi7ywkVNV55LhVgU0GaWWMOdk=
hash: jZzMXbxSnIsuAiEBqDZulZ/wCrrpW9bRLMZ6QYxs0Gk=
hash: uthKDtpuYHgn+wRsokVptSysyqBzmr4RP86mOC703bg=
you might need to change the has below to be the second one in the list for the code to pass
QA
hash: LX6ZGwP3Uii+KCZxDxDWlDWijvNI6K/t2906cUzKYM4=
hash: 4vkhpuZeIkPQ+6k0lXGi7ywkVNV55LhVgU0GaWWMOdk=
hash: jZzMXbxSnIsuAiEBqDZulZ/wCrrpW9bRLMZ6QYxs0Gk=
*/
pinner.debugMode = true
pinner.addCertificateHash(WebServiceConstants.HTTPStrings.hashKey)
return pinner
}
Using pinning library : https://github.com/nicwise/certificatepinner
I see several bugs in that code.
The very first line in your authentication handler is going to cause failures if you're behind an authenticated proxy.
It will also fail if the server wants any sort of HTTP auth password or OAuth credential.
It will also fail in a number of other situations.
You should never cancel an authentication request unless the request is actually bad in some way. Canceling the authentication request also prevents the operating system from handling it transparently for you if you can. The only situation where you should cancel an authentication request is if you check a cert or whatever and it is actually invalid. Otherwise, you should generally trust the OS to do the right thing by default when you request external handling.
So use default handling unless the authentication method is server trust.
The code does not check the authentication method at all. You should not be doing any checks unless the authentication method is server trust, for all of the reasons listed above. If it is anything else, use default handling. Always.
The next if statement has two problems:
It provides a new state for the authentication request without returning. This means you can call the completion handler afterwards, which could cause crashes and other misbehavior.
It is calling methods on the challenge sender that are intended to affect behavior. That's how you used to do it with NSURLConnection, but you should never call any methods on the challenge sender (other than possibly to see if it is an object of your own creation, if you are using custom NSURLProtocol classes) with NSURLSession, because it can cause all sorts of problems, up to and including crashes. (See the giant warning in the documentation for NSURLAuthenticationChallenge's -sender method, or, for that matter, the two paragraphs before that warning.)
I'm not entirely sure I trust the pinning code, either. It looks like it passes if any key within the chain of trust is a trusted key, whereas typically pinning requires that the last (leaf) key in the chain of trust be a trusted key.
The security advice in that pinning code is also dubious. You probably shouldn't be pinning to a certificate, but rather to the key inside the certificate. Pinning the leaf cert's key is entirely appropriate, and is really the only appropriate choice, because it is typically the only one whose key is actually under your direct control. If you reissue the cert, that's no big deal, because you should be reissuing with the same key as before, unless your key has been compromised in some way.
Alternatively, you can add a trust layer if you want, but this either requires running your own custom root (which would require changing the validateCertificateTrustChain method to add your custom root cert while validating the chain of trust) or convincing a CA to sell you a cert that can sign other certs, which costs $$$$. Neither of these options seems very practical.
Those issues make me a little bit concerned about the library as a whole, but I don't have time to audit it. You should probably ask around and see if anybody has done a thorough audit of the library in question, as it is notoriously easy to make mistakes when writing code that works with TLS keys, and I'd hate to see you run into security problems later.
After you fix all of the bugs listed above, if you're still having problems, come back and ask another question with updated code. Also, please also ask the code pinning project in question to fix their code snippets, as they seem to contain the same bugs. :-) Thanks.
I'd like to load a web page into a UIWebView, using URLSession.
An ephemeral session, indeed, because i wouldn't like to store the session if, for example, i've a login on Twitter/FB/whatever site.
So, i've a UIWebView named webPage
#IBOutlet var webPage: UIWebView!
a UITextField, where i can write the URL
#IBOutlet var urlField: UITextField!
Next to this text field, i've a UIButton, that implements this action
#IBAction func sendUrl(_ sender: Any) {
let stringUrl = urlField.text
let url = URL (string: stringUrl!)
openPageWithSession(url: url!)
}
and the function openPageWithSession, where i use the ephemeral session
func openPageWithSession(url: URL){
let request = URLRequest(url: url)
let ephemeralConfiguration = URLSessionConfiguration.ephemeral
let ephemeralSession = URLSession(configuration: ephemeralConfiguration)
let task = ephemeralSession.dataTask(with: request) { (data, response, error) in
if error == nil {
self.webPage.loadRequest(request)
} else {
print("ERROR: \(error)")
}
}
task.resume()
}
The loading of the web page it's ok. But if i've a login on Twitter and after that i kill the app, if i reopen the app and navigate Twitter again, i'm already logged in!
Why? The ephemeral session is not invalidate after a kill?
From the comments above.
Instead of self.webPage.loadRequest(request), you should use:
self.webView.load(data, mimeType: "text/html", textEncodingName: "", baseURL: url).
This way you'll be using the ephemeral session you created. Otherwise you're just loading the web view normally, without the ephemeral session at all.
I am currently working on a IOS App using Swift 3. I am working on the login system. Verification of logging in works fine. The logic is that, if login succeeds, it goes to the next Screen. However, if user does not exist, it should display an error message with UIAlert. But when I try to display the UIAlert, i get an error that says "Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished]"
//Getting data from database
func getData() -> Void {
let url: String = "http://localhost/fridge_app/login.php" //this will be changed to the path where service.php lives
//created NSURL
let requestURL = NSURL(string: url)
//creating NSMutableURLRequest
var request = URLRequest(url: requestURL! as URL)
//setting the method to post
request.httpMethod = "POST"
//Getting values from textfield
let usernameVal = username.text
let passwordVal = password.text
//creating the post parameter by concatenating the keys and values from text field
let postString = "username=\(usernameVal!)&password=\(passwordVal!)";
print(postString)
request.httpBody = postString.data(using: String.Encoding.utf8)
//creating a task to send the post request
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
//exiting if there is some error
if error != nil{
print("error is \(error)")
return;
}
// Print out response string
var responseString: NSString?;
responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
if(responseString == "invalid"){
self.isValid = false;
print(self.isValid)
}
if self.checkLogin(data: responseString!) == true {
self.performSegue(withIdentifier: "profileViewController", sender: self)
}
else{
print("Hello")
// It prints hello fine, but when it tries to run the showAlert function it fails
self.showAlert()
}
//print("responseString = \(self.responseString)")
}
//executing the task
task.resume()
}
This is the alert function
/*
* Show UIAlert Message
*/
func showAlert() -> Void{
let alert = UIAlertController(title: "User Does Not Exist",
message: "",
preferredStyle: UIAlertControllerStyle.alert)
let loginFail = UIAlertAction(title: "Close", style: .default, handler: nil);
alert.addAction(loginFail);
present(alert, animated: true)
}
This is method is called when user clicks login.
Unless you take special steps, the completion handlers for the tasks you submit to NSURLSession get run on a background thread. That means that any UI calls you do must be sent to the main thread or they don't work correctly (And may crash your app.)
The code in your completion handler is doing more UI work than just invoking an alert. You're also invoking a segue. If your completion handler doesn't do time-consuming work you might want to wrap the whole thing in a GCD call to the main thread:
DispatchQueue.main.async() {
//Put the entire body of your completion handler in here
}
Otherwise, you'll need to individually wrap each UI call in a call to the main queue like above.
EDIT:
Looking at your specific completion handler, you have one if/then/else block that does UIKit calls:
if self.checkLogin(data: responseString!) {
self.performSegue(withIdentifier: "profileViewController",
sender: self)
} else {
print("Hello")
self.showAlert()
}
So just wrap that part in a call to the main queue:
DispatchQueue.main.async() {
if self.checkLogin(data: responseString!) {
self.performSegue(withIdentifier: "profileViewController",
sender: self)
} else {
print("Hello")
self.showAlert()
}
}
I'm having this weird issue in which a newly created URLSessionUploadTask gets cancelled instantly. I'm not sure if it's a bug with the current beta of Xcode 8.
I suspect it might be a bug because the code I'm about to post ran fine exactly once. No changes were made to it afterwards and then it simply stopped working. Yes, it literally ran once, and then it stopped working. I will post the error near the end.
I will post the code below, but first I will summarize how the logic here works.
My test, or user-exposed API (IE for use in Playgrounds or directly on apps), calls the authorize method. This authorize method will in turn call buildPOSTTask, which will construct a valid URL and return a URLSessionUploadTask to be used by the authorize method.
With that said, the code is below:
The session:
internal let urlSession = URLSession(configuration: .default)
Function to create an upload task:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data? = nil
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
The authentication function, which uses a task created by the above function:
public func authorize(withCode code: String?, completion: AccessTokenExchangeCompletionHandler) {
// I have removed a lot of irrelevant code here, such as the dictionary building code, to make this snippet shorter.
let obtainTokenTask = buildPOSTTask(onURLSession: self.urlSession, appendingPath: "auth/access_token", withPostParameters: nil, getParameters: body, httpHeaders: nil) { (data, response, error) in
if let err = error {
completion(error: err)
} else {
print("Response is \(response)")
completion(error: nil)
}
}
obtainTokenTask.resume()
}
I caught this error in a test:
let testUser = Anilist(grantType: grant, name: "Test Session")
let exp = expectation(withDescription: "Waiting for authorization")
testUser.authorize(withCode: "a valid code") { (error) in
if let er = error {
XCTFail("Authentication error: \(er.localizedDescription)")
}
exp.fulfill()
}
self.waitForExpectations(withTimeout: 5) { (err) in
if let error = err {
XCTFail(error.localizedDescription)
}
}
It always fails instantly with this error:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED,
NSLocalizedDescription=cancelled,
NSErrorFailingURLStringKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED}
Here's a few things to keep in mind:
The URL used by the session is valid.
All credentials are valid.
It fails instantly with a "cancelled" error, that simply did not happen before. I am not cancelling the task anywhere, so it's being cancelled by the system.
It also fails on Playgrounds with indefinite execution enabled. This is not limited to my tests.
Here's a list of things I have tried:
Because I suspect this is a bug, I first tried to clean my project, delete derived data, and reset all simulators. None of them worked.
Even went as far restarting my Mac...
Under the small suspicion that the upload task was getting deallocated due to it not having any strong pointers, and in turn calling cancel, I also rewrote authorize to return the task created by buildPOSTTask and assigned it to a variable in my test. The task was still getting cancelled.
Things I have yet to try (but I will accept any other ideas as I work through these):
Run it on a physical device. Currently downloading iOS 10 on an iPad as this is an iOS 10 project. EDIT: I just tried and it's not possible to do this.
I'm out of ideas of what to try. The generated logs don't seem to have any useful info.
EDIT:
I have decided to just post the entire project here. The thing will be open source anyway when it is finished, and the API credentials I got are for a test app.
ALCKit
After struggling non-stop with this for 6 days, and after googling non-stop for a solution, I'm really happy to say I have finally figured it out.
Turns out that, for whatever mysterious reason, the from: parameter in uploadTask(with:from:completionHandler) cannot be nil. Despite the fact that the parameter is marked as an optional Data, it gets cancelled instantly when it is missing. This is probably a bug on Apple's side, and I opened a bug when I couldn't get this to work, so I will update my bug report with this new information.
With that said, everything I had to do was to update my buildPOSTTask method to account for the possibility of the passed dictionary to be nil. With that in place, it works fine now:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
} else {
postParameters = Data()
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
Are you by any chance using a third party library such as Ensighten? I had the exact same problem in XCode 8 beta (works fine in XCode 7) and all of my blocks with nil parameters were causing crashes. Turns out it was the library doing some encoding causing the issue.
For me, this was a weak reference causing the issue, so I changed
completion: { [weak self] (response: Result<ResponseType, Error>)
to
completion: { [self] (response: Result<ResponseType, Error>)
I'm using Swift in Xcode 6.2 (beta) but had the same problem on the 6.1 release version. I'm trying to use NSURLSession and believe I have it set up correctly (see code below). The problem is that I have a delegate setup to deal with a redirect happening through the code. I actually need to capture the cookies prior to the final redirection and I'm doing this through the delegate:
func URLSession(_:NSURLSession, task:NSURLSessionTask, willPerformHTTPRedirection:NSHTTPURLResponse, newRequest:NSURLRequest, completionHandler:(NSURLRequest!) -> Void )
This works and I'm able to execute code successfully and capture the cookies I need. The problem is that I need to add task.cancel() at the end of the function or else it never seems to complete and return to the delegator (parent?) function. Because of this I lose the results from the redirect URL (although in my current project it is inconsequential). The strange thing is that this was working for a while and seemingly stopped. I don't believe I entered any code that changed it, but something had to happen. Below is the relevant code.
NSURLSession Function:
func callURL (a: String, b: String) -> Void {
// Define the URL
var url = NSURL(string: "https://mycorrecturl.com");
// Define the request object (via string)
var request = NSMutableURLRequest(URL: url!)
// Use default configuration
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
// Create the NSURLSession object with default configuration, and self as a delegate (so calls delegate method)
let session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)
// Change from default GET to POST (needed to call URL properly)
request.HTTPMethod = "POST"
// Construct my parameters to send in with the URL
var params = ["a":a, "b":b] as Dictionary<String, String>
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
// Do some other stuff after delegate has returned...
})
task.resume()
return
}
The delegate code:
func URLSession(_:NSURLSession, task:NSURLSessionTask, willPerformHTTPRedirection:NSHTTPURLResponse, newRequest:NSURLRequest, completionHandler:(NSURLRequest!) -> Void ) {
// Check Cookies
let url = NSURL(string: "https://mycorrecturl.com")
var all = NSHTTPCookie.cookiesWithResponseHeaderFields(willPerformHTTPRedirection.allHeaderFields, forURL: url!)
// Get the correct cookie
for cookie:NSHTTPCookie in all as [NSHTTPCookie] {
if cookie.name as String == "important_cookie" {
NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookie)
}
}
task.cancel()
}
It used to return to the calling function without calling task.cancel(). Is there anything that looks wrong with the code that would cause it to just hang in the delegate function if task.cancel() isn't called?
Edit: What code would I add to fix this.
If you are not canceling the request, your willPerformHTTPRedirection should call the completionHandler. As the documentation says, this completionHandler parameter is:
A block that your handler should call with either the value of the request parameter, a modified URL request object, or NULL to refuse the redirect and return the body of the redirect response.