Why works on swift 2 beta 1 not on beta 6

I'm using this piece of code to access a url with beta 1


let config = NSURLSessionConfiguration.defaultSessionConfiguration()

let userPasswordString = "hroman:javierA17"

let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)

let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(nil)

let authString = "Basic \(base64EncodedCredential)"

config.HTTPAdditionalHeaders = ["Authorization" : authString]

let session = NSURLSession(configuration: config)

var running = false

let url = NSURL(string: "http:/

let task = session.dataTaskWithURL(url!) {

(let data, let response, let error) in

if let httpResponse = response as? NSHTTPURLResponse {

let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)

print(dataString)

}

running = false

}

running = true

task.resume()

while running {

print("waiting...")

sleep(1)

}


I changed a few things and it doesn't work


let config = NSURLSessionConfiguration.defaultSessionConfiguration()

let userPasswordString = "hroman:javierA17"

let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)

let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue:0))

let authString = "Basic \(base64EncodedCredential)"

config.HTTPAdditionalHeaders = ["Authorization" : authString]

let session = NSURLSession(configuration: config)

var running = false

let url = NSURL(string: "http:/

let task = session.dataTaskWithURL(url!) {

(let data, let response, let error) in

if let httpResponse = response as? NSHTTPURLResponse {

let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)

print(dataString)

}

running = false

}

running = true

task.resume()

while running {

print("waiting...")

sleep(1)

}


I included the parameter NSAllowsArbitraryLoads on the info.plist for both cases.

Answered by DTS Engineer in 49494022

What does this result mean? It's a problem of iOS 9?

What’s happening here is that iOS 9 delivers explicit authentication challenges (

NSURLAuthenticationMethodHTTPBasic
and
NSURLAuthenticationMethodHTTPDigest
) rather than the meta challenge that older systems used (
NSURLAuthenticationMethodDefault
). I was in a hurry so I just wrote code to handle the latter. Line 26 of my code should look like this:
if [NSURLAuthenticationMethodDefault, NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest].contains(challenge.protectionSpace.authenticationMethod) {

Unfortunately I can’t test what happens from there because the server now rejects the previous password:

$ curl -u hroman:javierA17 -is    http://apps.solu4b.com/beepinservice/BeepInDataService.svc | head -1
HTTP/1.1 401 Unauthorized

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

If your first code once pretended to work in some occasions, it was just a luck which you cannot rely on.


        while running {
            print("waiting...")
            sleep(1)
        }

This is a super-bad code to wait for asynchronous tasks to be completed.Watching a variable value, which may be changed in another thread, does not provide guaranteed definate result, even in C. (Or you may know how to use `volatile`.)

If you really need to notify the task completion, use semaphore.

var semaphore = dispatch_semaphore_create(0)
let url = NSURL(string: "http://...")
let task = session.dataTaskWithURL(url!) {
    (let data, let response, let error) in
    if let httpResponse = response as? NSHTTPURLResponse {
        let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
        print(dataString)
    }
    dispatch_semaphore_signal(semaphore)
}
task.resume()


print("waiting...")
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
print("wait end")

But before using semaphores, you'd better re-consider if you really need to wait for the task in the same method. In most cases, utilizing the completion handler in a good manner, you have no need to use a waiting semaphore.

(And I hope your code is not blocking the main thread...)


This is a little bit off topic, but this code:

let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue:0))

can be written like this:

let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions([])

OOPer

Maybe I wasn't clear. This is just an example of the problem I'm having and I understand your comments about asynchronous tasks.

I changed a line as you suggested

let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions([])

but the problem remains.

The key thing is the url is not authorizing the connection...but the same code (almost) works on Xcode 6.4 and not in Xcode 7 beta 6.

I'm receiving this message with beta 6

401 - Unauthorized: Access is denied due to invalid credentials.

Thanks for a kind explanation with my out-of-focus reply. Please take it as a needed process to eliminate irrelevant things.


As far as I tested your code in my Xcode 7 beta 6, it worked pretty well and I couldn't get 401 responses except when I sent mismatching credentials to my testing server.

(I added config.URLCache = nil to see the effects of changing the server settings immediately, but other parts are exactly the same as the semaphore version of the code above.)


I'm afraid your issue is caused by other problem(s) than using Xcode7. Have you checked your Xcode6 version of your code really works for the same server now?

OOper

Thanks for your answer.

I'm using XCode 7 beta 6 and Xcode 6.3 (6E36b)...but the problem remains..I've been trying with 2 different macs (MacBook Pro and the new MacBook) but the problem is the same. I putted the URLCache = nil but it wasn't a solution.

The server and credentials are:

let url = NSURL(string: "http://apps.solu4b.com/beepinservice/BeepInDataService.svc")

let userPasswordString = "hroman:javierA17"

For Xcode 6.3 worked and the log said:

waiting...Optional(<?xml version="1.0" encoding="utf-8"?><service xml:base="http://apps.solu4b.com/beepinservice/BeepInDataService.svc/" xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom"><workspace><atom:title>Default</atom:title><collection href="SN_TB_ASISTENCIA_EMPLEADO"><atom:title>SN_TB_ASISTENCIA_EMPLEADO</atom:title></collection></workspace></service>)

For Xcode 7 Beta 6 didn't worked and the log said:

waiting...

Optional(<!DOCTYPE html PUBLIC "-/

<html xmlns="http:/

<head>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>

<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>

<style type="text/css">

<!--

body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}

fieldset{padding:0 15px 10px 15px;}

h1{font-size:2.4em;margin:0;color:#FFF;}

h2{font-size:1.7em;margin:0;color:#CC0000;}

h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}

#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;

background-color:#555555;}

#content{margin:0 0 0 2%;position:relative;}

.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}

-->

</style>

</head>

<body>

<div id="header"><h1>Server Error</h1></div>

<div id="content">

<div class="content-container"><fieldset>

<h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>

<h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>

</fieldset></div>

</div>

</body>

</html>

)

It’s hard to say exactly what’s going on here without looking at a packet trace, but you’re definitely dealing with authentication incorrectly. In general, the NSURLSessions subsystem ‘owns’ the

Authorization
header of the HTTP[S] requests they send. You shouldn’t be manually trying to set up the header. Rather, you should handle authentication challenges via the authentication challenge delegate callbacks.

Here’s an example of how to handle the authentication challenge posed by that server.

class MyClass: NSObject, NSURLSessionDelegate {

    var session: NSURLSession!

    @IBAction func testAction(sender: AnyObject) {
        if self.session == nil {
            let config = NSURLSessionConfiguration.defaultSessionConfiguration()
            config.requestCachePolicy = .ReloadIgnoringLocalCacheData      // simplifies testing by forcing a new request each time
            self.session = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
        }
        self.session.dataTaskWithURL(NSURL(string: "http://apps.solu4b.com/beepinservice/BeepInDataService.svc")!) { (data, response, error) in
            guard error == nil else {
                NSLog("transport error %@ / %d", error!.domain, error!.code)
                return
            }
            guard let response = response as? NSHTTPURLResponse else {
                NSLog("not an HTTP response")
                return
            }
            NSLog("response: %d", response.statusCode)
        }.resume()
    }

    func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        NSLog("challenge %@", challenge.protectionSpace.authenticationMethod)
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault {
            let credential = NSURLCredential(user: "hroman", password: "javierA17", persistence: .ForSession)
            completionHandler(.UseCredential, credential)
        } else {
            completionHandler(.PerformDefaultHandling, nil);
        }
    }

}

I built this in Xcode 7.0b6 and tested the resulting program on OS X 10.9.5 [Actually, that was 10.10.5, not 10.9.5. Apparently my brain has regressed by a year.].

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Eskimo

I tested your suggestion with Xcode 7b6, OSX 10.10.4 but it gave me a 401 error wich is not correct.

I think I'll wait for the GM version, thanks anyway.

hroman

So, I created two versions of testing code, OS X Command Line tool and iOS Single VIew application.

And the tested result is as follows:


|code     |run on      |Local server|apps.solu4b.com|
|hroman's |OS X 10.10.4|Success     |Success        |
|hroman's |iOS 8.4 sim |Success     |Success        |
|hroman's |iOS 9.0 sim |Success     |401            |
|eskimo1's|OS X 10.10.4|-           |-              |
|eskimo1's|iOS 8.4 sim |Success     |Success        |
|eskimo1's|iOS 9.0 sim |401         |401            |

What does this result mean? It's a problem of iOS 9?


(Corrected result.)

Accepted Answer

What does this result mean? It's a problem of iOS 9?

What’s happening here is that iOS 9 delivers explicit authentication challenges (

NSURLAuthenticationMethodHTTPBasic
and
NSURLAuthenticationMethodHTTPDigest
) rather than the meta challenge that older systems used (
NSURLAuthenticationMethodDefault
). I was in a hurry so I just wrote code to handle the latter. Line 26 of my code should look like this:
if [NSURLAuthenticationMethodDefault, NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest].contains(challenge.protectionSpace.authenticationMethod) {

Unfortunately I can’t test what happens from there because the server now rejects the previous password:

$ curl -u hroman:javierA17 -is    http://apps.solu4b.com/beepinservice/BeepInDataService.svc | head -1
HTTP/1.1 401 Unauthorized

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Eskimo

Great!...you're right with your recommendation, I just tested and it worked with the line


if [NSURLAuthenticationMethodDefault, NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest].contains(challenge.protectionSpace.authenticationMethod) {


It's good to know this difference between the older iOS and iOS9.

Thanks a lot.

It seems you have succeeded to make the code work with your server.


In fact, I couldn't have succeeded to make it work, but it may be another issue than yours.

I'll study the new communications feature of iOS 9 again and retry sometime later.


Thanks for giving me a chance to find new features of iOS 9.

Why works on swift 2 beta 1 not on beta 6
 
 
Q