Swift 3, URLSession dataTask completionHandler not

2019-02-07 08:55发布

问题:

I am writing a library, So not using UIKit, Even in my iOS app same code works, but when i execute in command line in doesn't . In PlayGround also it seems working.

For some reason callback is not getting triggered, so print statements are not executing.

internal class func post(request: URLRequest, responseCallback: @escaping (Bool, AnyObject?) -> ()) {
    execTask(request: request, taskCallback: { (status, resp)  -> Void in
            responseCallback(status, resp)
    })
}

internal class func clientURLRequest(url: URL, path: String, method: RequestMethod.RawValue,  params: Dictionary<String, Any>? = nil) -> URLRequest {
    var request = URLRequest(url: url)
    request.httpMethod = method
    do {
        let jsonData = try JSONSerialization.data(withJSONObject: (params! as [String : Any]), options: .prettyPrinted)

        request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
        request.httpBody = jsonData
    } catch let error as NSError {
        print(error)
    }
    return request
}

private class func execTask(request: URLRequest, taskCallback: @escaping (Bool,
    AnyObject?) -> ()) {

    let session = URLSession(configuration: URLSessionConfiguration.default)
    print("THIS LINE IS PRINTED")
    let task = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
        if let data = data {
            print("THIS ONE IS NOT PRINTED")
            let json = try? JSONSerialization.jsonObject(with: data, options: [])
            if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode {
                taskCallback(true, json as AnyObject?)
            } else {
                taskCallback(false, json as AnyObject?)
            }
        }
    })
    task.resume()
}

Edits -: I am writing a library, So not using UIKit, Even in my iOS app same code works, but when i execute in command line in doesn't . In PlayGround also it seems working.

回答1:

I made a simple App from scratch. (Xcode 8 beta 6 / swift 3) In controller I pasted Your code. (plus url creation..) I see all in debugger:

THIS ONE IS PRINTED

THIS ONE IS PRINTED, TOO

I AM BACK

so it seems workin.

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let URLString = "https://apple.com"
        let url = URL(string: URLString)
        let request = URLRequest(url: url!)


        ViewController.execTask(request: request) { (ok, obj) in

            print("I AM BACK")

        }

    }

    private class func execTask(request: URLRequest, taskCallback: @escaping (Bool,
        AnyObject?) -> ()) {

        let session = URLSession(configuration: URLSessionConfiguration.default)
        print("THIS LINE IS PRINTED")
        let task = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
            if let data = data {
                print("THIS ONE IS PRINTED, TOO")
                let json = try? JSONSerialization.jsonObject(with: data, options: [])
                if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode {
                    taskCallback(true, json as AnyObject?)
                } else {
                    taskCallback(false, json as AnyObject?)
                }
            }
        })
        task.resume()
    }

}


回答2:

Did the changes suggested here, It works now.

Using NSURLSession from a Swift command line program

var sema = DispatchSemaphore( value: 0 )

private func execTask(request: URLRequest, taskCallback: @escaping (Bool,
    AnyObject?) -> ()) {

    let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil )

    session.dataTask(with: request) {(data, response, error) -> Void in
        if let data = data {
            let json = try? JSONSerialization.jsonObject(with: data, options: [])
            if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode {
                taskCallback(true, json as AnyObject?)
            } else {
                taskCallback(false, json as AnyObject?)
            }
        }
    }.resume()
    sema.wait()
}


回答3:

I know its late for the answer but in case you have not figure out the issue or getting issue at other places, lets try this.

You need to save session variable outside method scope (make it a instance variable). Since you defined it locally in function scope. Its get deallocated before completion handler can be called, remember completion handler can't retain your session object and after execution of run loop, garbage collector will dealloc your session object. We need to retain such objects whenever we want call back from delegates or from completion handler..

self.session = URLSession(configuration: URLSessionConfiguration.default)


回答4:

let dataTask = session.dataTask(with: request, completionHandler: {data, response,error -> Void in 
    print("Request : \(response)")

    let res = response as! HTTPURLResponse

    print("Status Code : \(res.statusCode)")

    let strResponse = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
    print("Response String :\(strResponse)")
    })
dataTask.resume()


回答5:

Swift 3.0

Just copy below code into your view controller.

@IBAction func btnNewApplicationPressed (_ sender: UIButton) {
        callWebService()
}

func callWebService() {
    // Show MBProgressHUD Here
    var config                              :URLSessionConfiguration!
    var urlSession                          :URLSession!

    config = URLSessionConfiguration.default
    urlSession = URLSession(configuration: config)

    // MARK:- HeaderField
    let HTTPHeaderField_ContentType         = "Content-Type"

    // MARK:- ContentType
    let ContentType_ApplicationJson         = "application/json"

    //MARK: HTTPMethod
    let HTTPMethod_Get                      = "GET"

    let callURL = URL.init(string: "https://itunes.apple.com/in/rss/newapplications/limit=10/json")

    var request = URLRequest.init(url: callURL!)

    request.timeoutInterval = 60.0 // TimeoutInterval in Second
    request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData
    request.addValue(ContentType_ApplicationJson, forHTTPHeaderField: HTTPHeaderField_ContentType)
    request.httpMethod = HTTPMethod_Get

    let dataTask = urlSession.dataTask(with: request) { (data,response,error) in
        if error != nil{
            return
        }
        do {
            let resultJson = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:AnyObject]
            print("Result",resultJson!)
        } catch {
            print("Error -> \(error)")
        }
    }

    dataTask.resume()
}


回答6:

Sometimes, for me, the solution when completionHandler were not called in these cases was because the flag "Allow Arbitrary loads" on Info.plist was defined as NO.

Allow Arbitrary loads flag defined as YES