Hi,
I'm new to Xcode and Swift and I want to create a simple app which can get data from a webserver via HTTP Requests. To test this app, I created a Command Line-Project in Xcode. I set up a very simple webserver using Python Flask and searched for code to send URL Requests.
I found a code, changed it a bit and tested it in a Swift Playground, and everything worked. So, now I tried to run the same code in a "Command Line Tool" - Project - but nothing happened. After debugging, I found that the "task.resume()"-Function does not execute the code in the "task" anymore. I also exported the project to an executable app, but it still didn't worked.
Can someone tell me why one code is working in a Swift Playground but not in a real project? (I just put the code 1:1 into the "main"-file)
Here's the code:
import Foundation
// my Flask-URL
guard let myUrl = URL(string: "http://127.0.0.1:5000/get") else {fatalError()}
var myReq = URLRequest(url: myUrl)
myReq.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myReq) { (data, response, error) in
print("Task running...") // This was printed in the playground
// but not in the Command Line App
if let error = error {
print("Error: \(error)")
return
}
if let response = response as? HTTPURLResponse{
print("Status: \(response.statusCode)")
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Value: \(dataString)")
}
}
task.resume()
Please tell me what I did wrong!
Tom the axolotl
URLSession
is an asynchronous API, that is, when you call resume()
the request starts and then, at some point in the future, it completes and calls your completion handler. When you put that code in a command-line tool, you end up running off the end of ‘main’ function, at which point the tool terminates before the network request is complete.
It’s easier to see this if you structure your main.swift
like this:
func main() {
… your existing code …
}
main()
When main
returns, the tool terminates and you’re done.
The standard workaround is to add a call to dispatchMain()
which ‘parks’ the main thread, preventing the tool from exiting. If you then want to exit after your network request is complete, call exit(_:)
directly.
So:
func main() {
… your existing code …
}
main()
dispatchMain()
Alternatively, use Swift 5’s new async/await support and make your main function an async function.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"