URLSession provides an asynchronous interface where a closure is called once the abstracted network call has completed. We’re all familiar with this, and it has encouraged us to write our own network layers in a similar fashion. A typical network call might be defined as:

I left a blank line at the end for those of you whose scroll bars don’t hide automatically

Consider a scenario where you need to make multiple API calls, one after another, such as the following:

After login, request the latest Terms and Conditions. If the User has not already accepted them, show the Terms and Conditions screen. Otherwise, proceed to the Dashboard.

To achieve this, you’ll call the Terms and Conditions API inside of the Login API’s completion closure. You’ll also need to show a loading indicator on the screen and handle any errors that may occur. In the end, the resulting code can be quite long:

Aside from being long, there’s a few things that I feel could be done in a better way:

[weak self] must be manually handled each time

must be manually handled each time A switch is always needed to check if an API call was successful or not

is always needed to check if an API call was successful or not isLoading must manually be handled. We set it to false when the login call completes prematurely: in some cases this may result in the loading animation resetting just before the terms and conditions call begins

must manually be handled. We set it to when the login call completes prematurely: in some cases this may result in the loading animation resetting just before the terms and conditions call begins The code doesn’t read from top to bottom. You can’t tell by looking only at login(with:) that a scene will be presented if the call is successful

What if we were able to make these two API calls synchronously, one after another?

(assuming you are using Combine or similar to subscribe to isLoading. More about this later)

With the above:

There’s no need to always get a weak reference to self

reference to self All errors are handled in the catch — there’s no need to switch on a Result<Response, Error>

on a Setting the isLoading flag is out of the way of our important logic. Essentially, we have simply said “show a loading indicator whilst this function is running”

flag is out of the way of our important logic. Essentially, we have simply said “show a loading indicator whilst this function is running” The code is concise and reads from top to bottom

How can we make this a reality?

The first thing we need to do is convert URLSession.dataTask(with:completionHandler:) into a synchronous function. If you’ve been using Swift for scripting, you may have done this before. I decided to use a Semaphore to freeze the calling thread until the task has returned a value:

The rest of your network code can remain pretty much the same as it normally is — with the added benefit of not needing to pass around completion closures! Simply return the parsed models from the function and throw any errors that occur.

Of course, we definitely don’t want to freeze the main thread while network operations are happening. I’ve been doing this by assuming all my main logic will be run in the background. When a button press calls a function on my Controller*, I immediately move to a new thread using a simple function I wrote. async(_:) exists in a protocol so that it can pass a weakened reference to Self into the closure:

In this way, I know that everything inside a function prefixed with async is safely run on a background thread. Of course, annotating a function as @async would be better than maintaining two functions with the same signature… but we may need to wait for another Swift version for that to work.

*Controller is what I call my ViewModel object. I feel the term “ViewModel” is misleading as a “model” is typically static storage…

Calling back to the main thread

Of course, we can’t update the UI from the background. However, there are many ways we can beautifully avoid making our code unreadable when we switch back to the main thread. I actually used this pattern before WWDC19 using my own observable type, but now with Combine it’s super simple to ensure all subscribers of a publisher are notified on a main thread with receive(on:) . Example in the next section:

Putting it all together

Suppose we have a super simple app where the user presses a button that downloads and displays their latest message. First we need a ViewModel that represents all the content on the screen:

Then we need a Controller to be in charge of downloading the latest messages and displaying the most recent one:

We could even do something smart where the async function in the Controller automatically sets the isLoading property to true when the thread is spawned and back to false when the thread dies 😉

And finally, a ContentView binds itself to Controller.viewModel and forwards button press actions:

Is it actually better?

Yes. Maybe? Maybe not. There’s a few things to consider, of course. I’ve already solved the problem of making two network calls at once in a strongly typed way here, but I’m sure there are other cases I haven’t come across yet. It’s worth noting an async/await style operation is being discussed by Chris Lattner et al., so this syntax may become canon at some point soon.