Command Line Swift: How to execute asynchronous requests

Photo by Markus Spiske on unsplash.com

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:

swift cli-simple-async-not-working.swift

on the command line would only result in:

Fetching data…

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

swift cli-simple-async-working.swift

it would this time result in an output like:

Fetching data…
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 dispatch_semaphore_wait(_:_:).

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.

iOS- / Mobile Developer