Skip to content

Instantly share code, notes, and snippets.

@sgharms
Forked from anonymous/intro.md
Last active June 3, 2021 13:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sgharms/3b0d7239dcb0623100b862a5fa6a2730 to your computer and use it in GitHub Desktop.
Save sgharms/3b0d7239dcb0623100b862a5fa6a2730 to your computer and use it in GitHub Desktop.
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
Copy link

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
Copy link
Author

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