At this point I'm going to have to confess that I need some help clarifying my model.
Quite. There two real issues here:
* You don’t need to create a queue just to start a request. It’s fine to start the request from any context. Thus, your `queue.async(…)` call is not needed.
* URLSession will run requests asynchronously, thus you need to prevent your process from terminating until the relevant requests are done. In a real program the framework you’re using typically does this for you. In a test project like this you have two options:
- dispatchMain() — That is, just add a line containing `dispatchMain()` to the bottom of your ‘main’ code.
- synthetic synchronous — Traditionally this is done using dispatch semaphores; I won’t show it here because a) you’ll find lots of example of it, and b) it’s not recommended, for obscure reasons related to QoS propagation.
1. How did point "y" get fired twice? Or how did it happen not at all?
I ran your code verbatim (except that I added a `dispatchMain()` at the end) on my Mac running 10.11.6, built with Xcode 8, and I didn’t see the second copy of `y`.
2. How did my callback for dataTask *never* fire? Even if the connection task *failed* the handler ought have fired, no?
Because the process terminated before the request finished.
3. I didn't want to bring this whole queue business into the picture, but it appears that without it the program exits before the handler has a chance to fire.
Correct.
4. Changing to a queue.sync from queue.async consistently produces the output I expect, but does not fire my completionHandler still.
Right, because that’s about how the request /starts/, not about how the request’s completion handler runs.
5. Is there a canonical reference for doing this simplest of tasks?
Here’s a minimal example of a command line tool that fetches a URL.
···
On 27 Oct 2016, at 13:01, Steven Harms via swift-users <swift-users@swift.org> wrote:
---------------------------------------------------------------------------
import Foundation
URLSession.shared.dataTask(with: URL(string: "http://www.example.com")!) { (data, response, error) in
if let error = error {
print("error: \(error)")
exit(1)
} else {
let response = response as! HTTPURLResponse
let data = data!
print("status: \(response.statusCode)")
for (key, value) in response.allHeaderFields {
print("header: \(key) = \(value)")
}
print("body: \(data as NSData)")
exit(0)
}
}.resume()
dispatchMain()
---------------------------------------------------------------------------
When I run this (same environment as above) I get this:
status: 200
header: Date = Fri, 28 Oct 2016 08:02:04 GMT
header: Content-Length = 606
header: Etag = "359670651"
header: x-ec-custom-error = 1
header: Last-Modified = Fri, 09 Aug 2013 23:54:35 GMT
header: Accept-Ranges = bytes
header: Vary = Accept-Encoding
header: X-Cache = HIT
header: Content-Type = text/html
header: Expires = Fri, 04 Nov 2016 08:02:04 GMT
header: Server = ECS (ewr/15BD)
header: Content-Encoding = gzip
header: Cache-Control = max-age=604800
body: <3c21646f 63747970 65206874 6d6c3e0a 3c68746d …>
The only weird thing in the code above is that `key` is of type `AnyHashable` rather than `String`. That’s because the type of `allHeaderFields` is wrong <rdar://problem/27805863>.
Alas, I’m not set up to run this on Linux )-:
Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware