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.

xrage
  • 4,690
  • 4
  • 25
  • 31
  • 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. – Eric Aya Aug 20 '16 at 11:30
  • Please show us how you call your `dataTask` private method. – Eric Aya Aug 20 '16 at 11:31
  • 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. – xrage Aug 20 '16 at 11:32
  • 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" – xrage Aug 20 '16 at 11:42
  • OK, I will edit this post in a minute after refactoring suggested by you. – xrage Aug 20 '16 at 11:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/121425/discussion-between-xrage-and-eric-aya). – xrage Aug 20 '16 at 11:54
  • 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`. – Eric Aya Aug 20 '16 at 13:08
  • I am not in playground, just for testing i used PG. Still in code its not working. – xrage Aug 20 '16 at 13:11
  • As I told you in my deleted answer, it works: https://www.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. – Eric Aya Aug 20 '16 at 13:17

6 Answers6

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

}
ingconti
  • 10,876
  • 3
  • 61
  • 48
  • 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. – xrage Aug 21 '16 at 10:38
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)
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
Ankit
  • 1,684
  • 14
  • 14
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()
}
Community
  • 1
  • 1
xrage
  • 4,690
  • 4
  • 25
  • 31
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()
Trasplazio Garzuglio
  • 3,535
  • 2
  • 25
  • 25
-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()
}
Community
  • 1
  • 1
Vivek
  • 4,916
  • 35
  • 40
  • 10
    consider explaining your answer. code-only answers are discouraged. – ADyson Jan 13 '17 at 11:28
  • tried this answer, did not work http://stackoverflow.com/questions/42481033/swifthttp-library-not-making-the-http-call#42481033 – Siddharth Feb 27 '17 at 09:32
  • 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. – Juanjo Aug 08 '17 at 13:15
-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