Swift 3, URLSession dataTask completionHandler not called
Asked Answered
S

6

7

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.

Sprage answered 20/8, 2016 at 11:27 Comment(9)
But if Both print statements in below code is not executing then this is not related to the the callback... it's just that your dataTask method is never called.Abstemious
Please show us how you call your dataTask private method.Abstemious
called it from another method. variable task is getting created then it reaches to resume(). but completionHandler is not executing. Adding other code in question itself.Sprage
I have added whole code now in question, changed the way you suggested. Still not working. FYI i am using xcode-beta-6 "session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in"Sprage
OK, I will edit this post in a minute after refactoring suggested by you.Sprage
Let us continue this discussion in chat.Sprage
As we have discovered in chat, you forgot to tell that you are in a Playground. You just have to enable asynchronous mode by importing PlaygroundSupport then doing PlaygroundPage.current.needsIndefiniteExecution = true.Abstemious
I am not in playground, just for testing i used PG. Still in code its not working.Sprage
As I told you in my deleted answer, it works: evernote.com/l/AFnkayGw4hlLhrclKEayHzUfb2oYo3kxldk so I don't see what is your question... Anyway, I've done enough. Read our chat again if you're not sure what is happening. Or maybe someone else will explain differently. Good luck.Abstemious
A
11

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

}
Andantino answered 20/8, 2016 at 14:15 Comment(1)
I am writing a command line utility, So not using UIKit, Even in my app same code works, but when i execute in command line in doesn't . In PlayGround also it seems working.Sprage
C
7

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)
Cogent answered 19/4, 2017 at 17:34 Comment(0)
S
5

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()
}
Sprage answered 21/8, 2016 at 12:10 Comment(1)
Very nice the explain of the taskCallBacks.Constrictor
R
1
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()
Radiogram answered 11/11, 2016 at 13:52 Comment(0)
D
-1

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()
}
Dobsonfly answered 13/1, 2017 at 11:8 Comment(3)
consider explaining your answer. code-only answers are discouraged.Harvin
tried this answer, did not work #42481533Rambow
First of all the task is not in the main thread. So anything in the UI needs to call DispatchQueue.main.async(execute:{code}) and second you need to add a callback or the call will return immediately. This is for Swift 3.Constrictor
H
-3

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

Headon answered 3/8, 2018 at 14:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.