Using several answers on SO, we have managed to write and execute a basic HTTP request:
import Foundation
let url:URL = URL(string: "http://jsonplaceholder.typicode.com/posts")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
let paramString = "data=Hello"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
guard let data = data, let _:URLResponse = response, error == nil else {
print("error")
return
}
let dataString: String = String(data: data, encoding: String.Encoding.utf8)!
print("here")
print("Data: \(dataString)")
print("Response: \(response!)")
}
task.resume()
while task.response == nil {}
print("Done")
You'll note that we already busy-wait until task.response
is set. However, neither data nor response are printed, only here
.
After endless trials with wrapping things this or that way we determine that we have a Heisenbug here: changing nothing in the code, sometimes here
is printed, sometimes nothing, and very, very rarely dataString
(let alone response
).
So we insert sleep(3)
before print("Done")
and, wonder of wonders, we get all prints.
Then we yelled a little bit (I may actually have thrown something), thought briefly about abandoning Swift altogether, but then calmed down enough to facepalm like sirs and post here.
Apparently, the main thread terminates whether or not any asynchronous tasks (threads?) are still running or not, killing all its spawn. How can we prevent that from happening, that is "join" the threads?
Bonus question: Does Alamofire deal with this behind the covers?
Using CwUtils by Matt Gallagher, I implemented a simple
CountdownLatch
which does the job:The most straight-forward (and built-in) way is probably to use a
DispatchSemaphore
:Active waiting seems to be the only way on the GCD. Using standard library material, this is what works: