-1

I'm trying to write my first Swift command line tool. The tool creates a network request and I want to print the progress of the request using URLSessionTaskDelegate.

The basic URL request code is here:

EDIT: updating more complete example

class ApiSession: NSObject, URLSessionTaskDelegate {
    func makeRequest(request: URLRequest,
                     callback: @escaping (Data?, URLResponse?, Error?) -> Void) {
        let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: .main)
        let task = urlSession.dataTask(with: request, completionHandler: callback)
        task.resume()
        urlSession.finishTasksAndInvalidate()
    }
    
    func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
        print("WAITING")
    }
    
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        print("TEST TEST")
    }
    
    func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
        print("BECAME INVALID")
    }
}

func main() throws {
    let apiSession = ApiSession()

    let request = URLRequest(url: URLComponents(string: "http://example.com/")!.url!)

    let sem = DispatchSemaphore(value: 0)
    apiSession.makeRequest(request: request) { (data, response, err) in
        if (err != nil) {
            print(err.debugDescription)
        }
        print(data)
        sem.signal()
    }

    // Code hangs here waiting for completion block to get called, but it never does when OperationQueue is main
    let _ = sem.wait(timeout: .distantFuture)
}

do {
    try main()
} catch (let error) {
    print("ERROR: \(error)")
}

When I use a basic URLSession object the request completes fine. However, if I attempt to pass in my own URLSessionTaskDelegate to print the bytesSent, the call never happens. Furthermore, if I set my delegateQueue to OperationQueue.main, the request itself never completes.

I'm sure there's some kind of concurrency issue here, but I have no idea where to start debugging this. Any help is appreciated!

Tyler
  • 889
  • 1
  • 11
  • 17

1 Answers1

0

Does your CLI app have a RunLoop defined? Without a RunLoop, it will exit and any pending tasks are deallocated when your main exits. Try adding something like:

while RunLoop.main.run(mode: .default, before: .distantFuture) {}

At the end of your main file.

  • It doesn't, but unfortunately the issue doesn't seem to relate to an exit. The code hangs at the semaphore wait since the request completion block is never called - but it gets called fine when the delegate/OperationQueue isn't present. – Tyler Aug 04 '20 at 22:42
  • I hadn’t noticed the semaphore, it looks it’s blocking the main thread, which would prevent the delegateQueue from calling back on it. If you change the delegateQueue to nil, do you get a callback? – Marc Lavergne Aug 05 '20 at 03:39