NSURL Response Status Meaning

Hi,

I'm fairly new to swift 3 but have done a couple practice apps where I'm going to web pages and pulling data. I started to make a new app but this time when I get the data back it is blank. If I look at the the task response [print(response)] I get the following:


Optional(<NSHTTPURLResponse: 0x61000002e6a0> { URL: https url goes here } { status code: 404, headers {"Strict-Transport-Security" = "max-age=86400";

} })


It seems there is an issue with the security of it being a HTTPS. I have the info.plist settings as:


App Transport Security Settings

Execption Domains

detroit.craigslist.org

NSIncludesSubdomains [Boolean - YES]

NSTemporaryExceptionAllowsInsecureHTTPSLoads [Boolean - YES]

Allow Arbitrary Loads [Boolean - NO]



My question is what can I do to resolve this error so I may pull data from the server? Thanks.




Here is my code:


let group = DispatchGroup()

group.enter()

if let url = URL(string: https url goes here)

let request = NSMutableURLRequest(url: url)

let task = URLSession.shared.dataTask(with: request as URLRequest) {

data, response, error in

if error != nil {

print("error")

} else {

if let unwrappedData = data {

let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)

print(dataString)

}

}

group.leave()

}

task.resume()

}

You need to accept the SSL certificate, and adapt

let task = URLSession.shared.dataTask(with: request as URLRequest) {.


have a look here:

h ttps://stackoverflow.com/questions/30741046/how-to-send-https-post-request-in-swift-using-nsurlsession

Perfect, thank you. I'm away from my computer but will try it later today and let you know. I notice in the link it says this blindly accepts all certificates. Is there a way to set it for just one website?

Once this works (I hope for you), you'll have to dig into documentation to see how to check the certificate.

Ok, so the solution you sent accepts all certificates. The more robust solution would be to determine the certificate and accept on a case by case basis. Thanks.

Let us know when it works and don't forget to mark the thread as closed.


And may be post the code of how you checked certificate. That will help others.

So I'm clearly doing this wrong.... I've put the code snippit in my code, but I'm getting two errors:


First bolded and underlined section: "use of local variable 'URLSession' before its declaration."


Second bolded and underlined section: "value of type (URLSession.URLSessionTask,URLAuthenticationChallenge....)->Void->() has no member AuthChallenge.."




class ViewController: UIViewController, URLSessionDelegate {


@IBAction func refreshData(_ sender: Any) {

let group = DispatchGroup()

group.enter()

if let url = URL(string: "url here") {

let request = NSMutableURLRequest(url: url)

let task = URLSession.shared.dataTask(with: request as URLRequest) {

data, response, error in

let session = URLSession(configuration: URLSessionConfiguration.defaultSessionConfiguration,

delegate: self,

delegateQueue: nil)

func URLSession(session: URLSession, task: URLSessionTask, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {

let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)

completionHandler(URLSession.AuthChallengeDisposition.UseCredential, credential)

}

}

if error != nil {

print("error")

} else {

if let unwrappedData = data {

let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)

print(dataString)

}

}

group.leave()

}

task.resume()

}

}

override func viewDidLoad() {

super.viewDidLoad()

/

}


override func didReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

/

}


}

First thing to do is get URLSession out of IBAction


    func URLSession(session: URLSession, task: URLSessionTask, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
       if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
         let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
         completionHandler(URLSession.AuthChallengeDisposition.UseCredential, credential)
       
    }

    @IBAction func refreshData(_ sender: Any) {

        let group = DispatchGroup()
        group.enter()
        if let url = URL(string: "url here") {
            let request = NSMutableURLRequest(url: url)
            let task = URLSession.shared.dataTask(with: request as URLRequest) {
                data, response, error in
                let session = URLSession(configuration: URLSessionConfiguration.defaultSessionConfiguration,
                                        delegate: self,
                                        delegateQueue: nil)



Tell how it goes like this

Worked well. Now just the second error of : "Value of type '(URLSession,URLSessionTask,URLAuthenticationChallenge,(URLSession.AuthChallengeDisposition,URLCredential?)->Void)-> ()' has no member 'AuthChallengeDisposition'

on the bolded line.


I think the error has to do with there being two 'URLSession' statements. there is the section: URLSession and then completionHandler: URLSession...



func URLSession(session: URLSession, task: URLSessionTask, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {

let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)

completionHandler(URLSession.AuthChallengeDisposition.UseCredential, credential)

}

}

Update: So I've cleaned up my code and taken another approach. No errors except it doesn't seem to go through the urlSession function. If I change my session line to session = urlSession then I do get errors for not having the right input labels.







func httpGet(request: URLRequest) {

let configuration = URLSessionConfiguration.default

let session = URLSession(configuration: configuration, delegate: self, delegateQueue:OperationQueue.main)

let task = session.dataTask(with: request){

(data, response, error) -> Void in

if error == nil {

let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!

print(dataString)

}

}

task.resume()

}

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

print("Accepting cert as always")

completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))

}

httpGet(request: URLRequest( url: URL(string: "web page here")!))

Can you insert print statement in various places, to see what's go on.


func httpGet(request: URLRequest) {
            let configuration = URLSessionConfiguration.default
          print("Start httpGet")
            let session = URLSession(configuration: configuration, delegate: self, delegateQueue:OperationQueue.main)
            let task = session.dataTask(with: request) {
                (data, response, error) -> Void in
                if error == nil {
                    let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
                    print(dataString)
                } else {
                         print("Error in session.dataTask", error)

                    }

            }
            task.resume()
        }
     
     
        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            print("Accepting cert as always")
            completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
        }

Ok I've put several markers in:


print("Marker 1")
       
        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential) -> Void) {
            print("Accepting cert as always")
            completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
           
            print("Marker 4")
           
        }
       
        print("Marker 2")
       
        func httpGet(request: URLRequest) {
            print("Marker 5")
           
            let configuration = URLSessionConfiguration.default
            let session = URLSession(configuration: configuration, delegate: self, delegateQueue:OperationQueue.main)
           
            print("Marker 6")
           
            let task = session.dataTask(with: request){
                (data, response, error) -> Void in
               
                print("Marker 7")
               
                if error == nil {
                    print("Marker 8")
                    let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
                    print(response)
                    print("Marker 9")
                    print(error)
                    print("Marker 10")
                    print(dataString)
                    print("Marker 11")
                   
                } else {
                   
                    print("Error in session.dataTask", error)
                   
                }
            }
            task.resume()
        }
       
        print("Marker 3")
       
        httpGet(request: URLRequest( url: URL(string: "url here")!))






Here is the output:


Marker 1

Marker 2

Marker 3

Marker 5

Marker 6

Marker 7

Marker 8

Optional(<NSHTTPURLResponse: 0x60800022a640> { URL: https:/

"Strict-Transport-Security" = "max-age=86400";

} })

Marker 9

nil

Marker 10

Marker 11

Read an interesting info here :cannot have delegate AND completion handler !

h ttps://stackoverflow.com/questions/26694651/nsurlsession-delegates-not-called


So, I propose to try the following, to see if delagate is called.

If yes, try to implement as proposed in the link above


     print("Marker 1")
  
        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential) -> Void) {
            print("Accepting cert as always")
            completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
      
            print("Marker 4")
      
        }
  
        print("Marker 2")
  
        func httpGet(request: URLRequest) {
            print("Marker 5")
      
            let configuration = URLSessionConfiguration.default
            let session = URLSession(configuration: configuration, delegate: self, delegateQueue:OperationQueue.main)
      
            print("Marker 6")
      
            let task = session.dataTask(with: request)          // May be you have to pass nil as completion handler here
/*         {
                (data, response, error) -> Void in
          
                print("Marker 7")
          
                if error == nil {
                    print("Marker 8")
                    let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
                    print(response)
                    print("Marker 9")
                    print(error)
                    print("Marker 10")
                    print(dataString)
                    print("Marker 11")
              
                } else {
              
                    print("Error in session.dataTask", error)
              
                }
            } */

            task.resume()
        }
  
        print("Marker 3")
  
        httpGet(request: URLRequest( url: URL(string: "url here")!))



Maybe this one gives a better answer :

h ttps://stackoverflow.com/questions/41428629/urlsession-delegate-success-methods-not-called-but-no-error


use URLSessionDownloadTask instead of URLSessionDataTask and use background for URLSessionConfiguration as follows:


var downloadTask: URLSessionDownloadTask?
var session: URLSession?

override func viewDidLoad() {
        super.viewDidLoad()

        let configuration = URLSessionConfiguration.background(withIdentifier: "backgroundSession")
        session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)

        let url = URL(string: "h ttps://unsplash.it/200/300/?random")!
        downloadTask = session?.downloadTask(with: url)
        downloadTask!.resume()
}

Hm, interesting. Stil no luck with either of those though:


        print("Marker 1")
     
        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential) -> Void) {
            print("Accepting cert as always")
            completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
         
            print("Marker 4")
         
        }
     
     
        print("Marker 2")
     
        func httpGet(webURL: URL) {
            print("Marker 5")
            let request: URLRequest = URLRequest(url: webURL) /
            let configuration = URLSessionConfiguration.background(withIdentifier: "backgroundSession")
            let session = URLSession(configuration: configuration, delegate: self, delegateQueue:OperationQueue.main)
         
            print("Marker 6")
         
            let task = session.downloadTask(with: webURL)
            print("Marker 7")
            task.resume()
        }
     
        print("Marker 3")
     
        httpGet(webURL: URL(string: "web url here")!)



Output:

Marker 1

Marker 2

Marker 3

Marker 5

Marker 6

Marker 7

Read an interesting info here: cannot have delegate AND completion handler!

That’s not the whole truth. Session delegate callbacks are divided into two groups:

  • Delegate callbacks on the main request path, most notably,

    urlSession(dataTask:didReceiveData:)
    and
    urlSession(_:task:didCompleteWithError:)
  • Others, including the authentication challenge delegate callbacks

The former won’t be delivered if you use the closure-based convenience API, but the latter will.

Consider pasted in at the end of this post. When runs this prints:

… start
… challenge NSURLAuthenticationMethodServerTrust
… success

So, you get the authentication challenge delegate callback even though you’re using the closure-based convenience API.

Notwithstanding the above, I think this thread is way (way way way way) off in the weeds. The correct response to encountering an HTTPS security error is not to disable HTTPS security. That defeats the whole purpose of doing HTTPS in the first place. You should try to figure out what’s causing the security problem and then either fix the server or, if that’s not possible, implement a secure client-side workaround.

If you want advice on that front you should post a reference to the special server that’s causing you problems. Or, if that server is not on the public Internet, let me know and I can post some suggestions about how to capture the info need to debug this.

Share and Enjoy

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

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

class MainViewController : UITableViewController, URLSessionDelegate {

    var session: URLSession!

    func start() {
        NSLog("start")
        if self.session == nil {
            let config = URLSessionConfiguration.default
            config.requestCachePolicy = .reloadIgnoringLocalCacheData
            self.session = URLSession(configuration: config, delegate: self, delegateQueue: .main)
        }
        let url = URL(string: "https://example.com")!
        let req = URLRequest(url: url)
        self.session.dataTask(with: req) { (data, response, error) in
            if let error = error as NSError? {
                NSLog("transport error: %@ / %d", error.domain, error.code)
                return
            }
            NSLog("success")
        }.resume()
    }

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        NSLog("challenge %@", challenge.protectionSpace.authenticationMethod)
        completionHandler(.performDefaultHandling, nil)
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.start()
        self.tableView.deselectRow(at: indexPath, animated: true)
    }
}

Hi Quinn,


I do agree, overpassing certificate would be a very bad thing (may lead to app being refused on Appstore ?). The point was just an intermediate development step to check it was possible to access the site.

Then, as I wrote :

"Once this works (I hope for you), you'll have to dig into documentation to see how to check the certificate."


Thanks for stressing the point.

Thanks and sorry for all the posts. I will try that code tonight. The project summary: I want to have the app go to Craigslist daily with given search parameter (Craigslist puts those parameters in the URL as shown below). Then if a result matches a search term I'm looking for, I want it to save that match and notify me. h ttps://detroit.craigslist.org/search/mcy?hasPic=1&search_distance=100&postal=48152&min_price=1000&max_price=4000&min_auto_year=1990&max_auto_year=2017&auto_title_status=1

Quinn,


Here's the new code with your format:


import UIKit
class ViewController: UIViewController, URLSessionDelegate {

    var session: URLSession!
    @IBAction func refreshData(_ sender: Any) {

        NSLog("start")
        if self.session == nil {
            let config = URLSessionConfiguration.default
            config.requestCachePolicy = .reloadIgnoringLocalCacheData
            self.session = URLSession(configuration: config, delegate: self, delegateQueue: .main)
        }
        let url = URL(string: "h t t p s : / / d e t r o i t . c r a i g s l i s t . o r g")!
        let req = URLRequest(url: url)
        self.session.dataTask(with: req) { (data, response, error) in
            if let error = error as NSError? {
                NSLog("transport error: %@ / %d", error.domain, error.code)
                return
            }
            NSLog("success")
            if let unwrappedData = data {
                let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
                print("Printing Response...")
                print(response)
                print("Printing Data...")
                print(dataString)

            }
     
            }.resume()


        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            NSLog("challenge %@", challenge.protectionSpace.authenticationMethod)
            completionHandler(.performDefaultHandling, nil)
        }

    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}



And the output:


2017-08-14 20:46:06.925 Craigslist[1191:26211] start

2017-08-14 20:46:07.429 Craigslist[1191:26211] success

Printing Response...

Optional(<NSHTTPURLResponse: 0x61800003ff00> { URL: h ttps:/

"Strict-Transport-Security" = "max-age=86400";

} })

Printing Data...

Optional(

)

2017-08-14 20:46:07.429 Craigslist[1191:26211] success

So that’s work, right? Which means you should be able to remove your authentication challenge handler entirely, because it’s doing nothing other than logging.

Share and Enjoy

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

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

Yes I'm getting the success, but as you can see by the output of (dataString) it's coming up blank despite there being stuff on the page. That's the issue I'm struggling with.

… as you can see by the output of (dataString) it's coming up blank despite there being stuff on the page. That's the issue I'm struggling with.

Ah, that’s a completely different kettle of fish. I’ll respond below (because this indentation is getting out of control)-:

Share and Enjoy

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

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

… as you can see by the output of (dataString) it's coming up blank despite there being stuff on the page. That's the issue I'm struggling with.

With reference to the code you posted on 15 Aug, there’s a bunch of potential issues:

  • First, you should be using Swift’s

    String
    here, not Foundation’s
    NSString
    . This isn’t the cause of your problem, but programming in Swift is easier if you stick with Swift’s standard data types.
  • Next, you shouldn’t look at the data passed to your completion handler without first looking at the response’s status code. The fact that

    error
    is nil only tells you that
    URLSession
    was able to send the request to a server and get a response back. It doesn’t tell you that the request was successful on the server. You should always check the response status code (like the
    statusCode
    property) to verify that the response is valid.
  • It’s also a good idea to check the response content type (via the

    mimeType
    property). In some cases (including captive networks and HTTP proxies) you can get a response that didn’t come from the origin server. In that case the body of the response may not be of the type you expect, and thus you should check for the type.

Putting all of the above together a typical completion handler should look like this:

let task = self.session.dataTask(with: request) { (data, response, error) in
    if let error = error {
        // … report transport error …
        return
    }
    let response = response as! HTTPURLResponse
    let data = data!
    guard response.statusCode == 200 else {
        // … report server error …
        return
    }
    guard response.mimeType == "text/plain" else {
        // … report server error …
        return
    }
    NSLog("%@", data as NSData)
}

Once you know the type of the data you can reasonably parse it. How you do that depends on the type itself. My guess is that you’re dealing with

text/html
data. Is that right?

Share and Enjoy

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

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

Ok that makes sense. So I was there was a connection, but seeing a blank rather than any error because I wasn't checking the response. I get no output, but when I check, it's getting hung up in the guard response.statusCode == 200 block.

So what’s

statusCode
?

Share and Enjoy

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

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

<NSHTTPURLResponse: 0x610000032800> { URL: https:/

"Strict-Transport-Security" = "max-age=86400";

} }

That’s a print out of the response object which, alas, does not show the status code. I’m looking for the

statusCode
property of that response, which you can fetch using the code from line 8 of the snippet in my 16 Aug post.

Share and Enjoy

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

let myEmail = "eskimo" + "1" + "@apple.com"
NSURL Response Status Meaning
 
 
Q