Swift can be a wonderful language, when it comes to command line tools and scripting, especially since it’s available on various platforms including Linux and Windows - but can also be tricky sometimes when you’re coming from iOS or macOS programming, as some things need to be done differently or might not work the way you would expect them.
Asynchronous request are one of such things when used in command line code…
Note: Swift 5.x or later is required and it’s assumed you already have some knowledge about programming in Swift. This is not a tutorial to teach you the basics.
Let’s take the following simple example:
This example fetches some data from example.com in a URLSession request and prints the result. The program will execute a HTTP call and fetch the HTML. Nothing fancy, but running this code by typing in:
on the command line would only result in:
What about the print inside the request (line 17)? 🤔
The problem here is that the program execution is not waiting for the completion of the asynchronous call the URLSession starts, instead it will exit before anything is received.
Fortunately, there is an easy fix for that: The DispatchSemaphore.
By simply adding a semaphore with some lines of code to our previous example we can make the call work:
Running this code
it would this time result in an output like:
Fetched data: 1256 bytes
Finally both print statements were executed and it seems we have fetched some data. But what happened?
The interesting changes are in the lines 11, 19 and 23.
Line 11 initialises our semaphore with a count of zero which would make it end waiting after the first signal received.
In line 23 we decrement the semaphore counter and wait for a signal to be send. As written in Apple’s Developer Documentation:
Decrement the counting semaphore. If the resulting value is less than zero, this function waits for a signal to occur before returning.
In line 19 we send out a signal which increments the semaphore and completes the execution. As written in Apple’s Developer Documentation:
Increment the counting semaphore. If the previous value was less than zero, this function wakes a thread currently waiting in
These simple changes are all we need to make our script work. This scheme can be applied every time you need to execute asynchronous code in your command line Swift code — not only URLSession requests.
It should be noted that this semaphore is not required in iOS or macOS GUI applications as they work differently in execution than command line programs.