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()

}

Replies

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.