Skip to content

Instantly share code, notes, and snippets.

@sgharms
Forked from anonymous/intro.md
Last active Jun 3, 2021
Embed
What would you like to do?
Learning to work with command-line Swift on Linux

The goal was to learn how to make a simple HTTP request in Swift using Swift 3 on Linux.

Obviously, this presents some logistical challenges but also is enlightening about the state of Swift as a language for general computing outside of the Apple tooling ecosystem. I got stumped and worked using only vim and the available documentation.

let queue = DispatchQueue.global(qos: .background)
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
print("staring sync")
queue.async(execute: {
print(">>> [\(queue.label)]: At the queue start")
print("x")
let task = session.dataTask(with: URL(string: "http://google.com")!, completionHandler: {
(data, response, error) in
print("Task ran!")
})
print("y")
task.resume()
print("z")
})
print("ending sync")
staring sync
ending sync
# or...
staring sync
ending sync
>>> [com.apple.root.background-qos]: At the queue start
x
y
y
z
Obviously, this was surprising so I posted a question to swift-users mailing list.
My general theory was that something was wrong with the asynchrony model, but the documentation on this is pretty much
awful. It's confusing between Swift 2 and Swift 3 and it's hard to tell what's required or not in order to make things work.
I think that this is somewhere where the Sift documentation is sorely lacking. I'll discuss this further.
/* The first reply that provided a useful hint at what was missing in my code came from Papushev */
import Foundation
import Dispatch
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
let urls = ["https://www.google.com", "https://www.apple.com"]
let group = DispatchGroup()
print("staring sync")
urls.forEach { url in
group.enter()
session.dataTask(with: URL(string: url)!, completionHandler: {
(data, response, error) in
print("Task ran! \(data)")
group.leave()
}).resume()
}
print("ending sync")
group.wait()
/* LEARNING:
1. I had most of the tools iI needed *whew*'
2. DispatchGroup is a thing that might be useful for keeping the app open until my async callbacks fire
*/
➜ octopress git:(master) ✗ swift --version
Swift version 3.0 (swift-3.0-RELEASE)
Target: x86_64-unknown-linux-gnu
➜ octopress git:(master) ✗ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
➜ octopress git:(master) ✗ swift rando.swift
staring sync
ending sync
Task ran! Optional(11679 bytes)
Task ran! Optional(36602 bytes)
➜ octopress git:(master) ✗

Both Papushev and Quinn "The Eskimo"!" suggested that instead of doing an explicit syncrhonization, I might want to leverage the dispatch_main method.

This is where the API documentation really fell on its face IMNSHO.

The Apple documentation really gives very little clue as to how to use this thing. Specifically, the only thing that the do suggests is that the function waits for sumitted tasks to complete. Unfortunately, the pdataTask:with:completionHandler documentation gives no indictation that it sumbmits to the main queue. I suppose that's guessable, but probably not desirable in documentation. The only reference to queues is in reference to a delegate -- which I did not use. I wound up going pretty far afield trying to process Quinn and Papushev's tip that I look into dispatch_main.

This SO post was better, althought its lede was buried in a comment.

/* Quinn's solution, slightly modified */
import Foundation
import Dispatch
let SITE = "http://www.nytimes.com"
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
session.dataTask(with: URL(string: SITE)!) { (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 Data)")
exit(0)
}
}.resume()
dispatchMain()
@andrerolfs

This comment has been minimized.

Copy link

@andrerolfs andrerolfs commented Apr 18, 2020

Thank you so much! I have spend hours trying to find out why my command line tool does not fire the request and your code using the Dispatchgroup solved it for me.

@sgharms

This comment has been minimized.

Copy link
Owner Author

@sgharms sgharms commented May 2, 2020

@andrerolfs You're welcome! I would have hoped the docs would be better by now, but you're not making me hopeful! Good luck.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment