'How to prevent a Command Line Tool from exiting before asynchronous operation completes

In a swift 2 command line tool (main.swift), I have the following:

import Foundation
print("yay")

var request = HTTPTask()
request.GET("http://www.stackoverflow.com", parameters: nil, completionHandler: {(response: HTTPResponse) in
    if let err = response.error {
        print("error: \(err.localizedDescription)")
        return //also notify app of failure as needed
    }
    if let data = response.responseObject as? NSData {
        let str = NSString(data: data, encoding: NSUTF8StringEncoding)
        print("response: \(str)") //prints the HTML of the page
    }
})

The console shows 'yay' and then exits (Program ended with exit code: 0), seemingly without ever waiting for the request to complete. How would I prevent this from happening?

The code is using swiftHTTP

I think I might need an NSRunLoop but there is no swift example



Solution 1:[1]

I realize this is an old question, but here is the solution I ended on. Using DispatchGroup.

let dispatchGroup = DispatchGroup()

for someItem in items {
    dispatchGroup.enter()
    doSomeAsyncWork(item: someItem) {
        dispatchGroup.leave()
    }
}

dispatchGroup.notify(queue: DispatchQueue.main) {
    exit(EXIT_SUCCESS)
}
dispatchMain()

Solution 2:[2]

Adding RunLoop.main.run() to the end of the file is one option. More info on another approach using a semaphore here

Solution 3:[3]

You can call dispatchMain() at the end of main. That runs the GCD main queue dispatcher and never returns so it will prevent the main thread from exiting. Then you just need to explicitly call exit() to exit the application when you are ready (otherwise the command line app will hang).

import Foundation

let url = URL(string:"http://www.stackoverflow.com")!
let dataTask = URLSession.shared.dataTask(with:url) { (data, response, error) in
    // handle the network response
    print("data=\(data)")
    print("response=\(response)")
    print("error=\(error)")

    // explicitly exit the program after response is handled
    exit(EXIT_SUCCESS)
}
dataTask.resume()

// Run GCD main dispatcher, this function never returns, call exit() elsewhere to quit the program or it will hang
dispatchMain()

Solution 4:[4]

Don't depend on timing.. You should try this

let sema = DispatchSemaphore(value: 0)

let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg")!

let task = URLSession.shared.dataTask(with: url) { data, response, error in
  print("after image is downloaded")

  // signals the process to continue
  sema.signal()
}

task.resume()

// sets the process to wait
sema.wait()

Solution 5:[5]

If your need isn't something that requires "production level" code but some quick experiment or a tryout of a piece of code, you can do it like this :

SWIFT 3

//put at the end of your main file
RunLoop.main.run(until: Date(timeIntervalSinceNow: 15))  //will run your app for 15 seconds only

More info : https://stackoverflow.com/a/40870157/469614


Please note that you shouldn't rely on fixed execution time in your architecture.

Solution 6:[6]

Swift 4: RunLoop.main.run()

At the end of your file

Solution 7:[7]

// Step 1: Add isDone global flag

var isDone = false
// Step 2: Set isDone to true in callback

request.GET(...) {
    ...
    isDone = true
}

// Step 3: Add waiting block at the end of code

while(!isDone) {
    // run your code for 0.1 second
    RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1))
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 skwashua
Solution 2 duan
Solution 3 Mike Vosseller
Solution 4 Sam Soffes
Solution 5 Community
Solution 6 Chris Karani
Solution 7