Synchronising the Asynchronous in Swift

Photo by Josh Calabrese on Unsplash

I’m sure you’ve already had been in situations, where you had asynchronous tasks and you had to make them execute synchronously, because there were dependencies between them. Like imagine the scenario of scheduling a meeting

  • calling some login API to get a token
  • get a list of available persons at certain time
  • get a person(s) details
  • create a meeting with persons available

Before you start shouting that this is just a poor example of API, rest assured I agree with you. But it’s our reality all too many times.

But, there is a solution out of the box: use Operations and link dependent tasks on the queue! That sounds like a text book example for what we want to achieve.

(NS)Operations

Let’s use the Operations and make it work:

Operation

We can see we have an Operation SumOfTwoAsyncOperations . We can clearly see its business logic performs the business logic within an async call on the global, backthread queue.

Function calling 2 dependent operations

And here we have a function sumOfTwoAsyncOperations, which takes 4 numbers and adds number1 and number2 in operation1 and number3 and number4 in operation2 . We can see that we also created a “dummy” operation3 of type BlankOperation with a sole purpose to confirm the last executed operation, which should be operation3 and print the message into the console.

So, if we now call this function:

with parameters 4 and 5, then we would expect to get this output in the console:

Operation 1 Sum of 4 + 4 = 8

Operation 2 Sum of 5 + 5 = 10

Operation 2 finished!

Well.. NOT!

We get this:

Operation 2 finished!

Operation 2 Sum of 5 + 5 = 10

Operation 1 Sum of 4 + 4 = 8

We get the opposite order, but even that is not guaranted. Actually, we have no real control over the results. If we remember discussion in this article, it was mentioned dependency management and some other Operations don’t come completely free or not free at all.

What actually happens is that we definitely start and execute operations in wished order, but since they are asynchronous, we don’t have control over when they actually finish, meaning, calling the callback.

Semaphores to the rescue!

OK, how about if we make operations synchronous, like this:

Synchronized asynchronous task/Operation

Yes, why not? As you can see, we’ve used semaphores, which are really an easy and probably the best way to do the job, especially because you can set the time limit thus make sure the thread doesn’t get blocked forever.

Never use any technique to potentially block the thread indefinitely. Always limit the wait time, making sure if for some reason task doesn’t finish, your app doesn’t freeze of gets unresponsive at any time.

also

Never block the main thread, considering dependencies it can happen all too quickly, that a casual bug somewhere else, burried deep, can eventually freeze the app.

For all this to work, we also need to make our operation asynchronous and override 2 standard methods as described in Apple’s documention:

I’d say, this is quite a hefty amount of work for something, what seemed almost like a trivial task. ☹️ Esentially, what this so-called isAsynchronousdoes, is telling the runtime to apply semaphores (most probably) before isFinishedchanges its value. Seems like a kind of old-fashion way of doing it…

Well, I would like to explore some other possibilities, too.


GCD

Let’s see, how would this look like with GCD and no ugly nesting:

Same 2 tasks using GCD and semaphores

… or, just the boilerplate:

This code does the same as the one before with Operations. Looks leaner and easier to read. It would definitely be my choice.

Dispatch Groups

We could do something similar with Dispatch Groups as well:

Same 2 tasks using GCD and Dispatch Groups

… or again, just with the boilerplate:

We can see that it doesn’t differ much from the example with semaphores before, you can also use wait with the timeout (which we actually didn’t really need here), but I would still rather choose the semaphores, simply because their purpose is clearer and fits the issue better.

Dispatch groups, however, should always be the weapon of choice, when a group of tasks needs to be executed before proceeding and the tasks don’t have mutual dependencies.

Promises we made

Yes, I hear the voices: this can be done better with the PromiseKit! I agree and disagree.

I love Promises! I love them in the languages with native support, like Javascript. They flatten potentially nesting structure into beautifully readable closures. However, if you want to pass parameters between the .thenclosures, then the code cold get ugly. At least until Ecmascript 8, when we got asyncand await keywords, which solve the problem.

But these are all language features. PromiseKit for Swift is an SDK feature, extending the classes. I have few problems with that approach:

  • neverending catch-up with iOS SDK story
  • very powerful dependency
  • complementing the language instead of adding the feature
  • not part of the Swift steering committee’s initiative (Evolution), meaning we can’t be sure the transition will be swift and easy and the code certainly won’t be included into the language itself
  • doesn’t offer asyncand await keywords out of the box, but you have a lovely wrapper from Yannick to handle this

I could list a few more, but my personal view is that all these in comparison to simple GCD semaphore steps is not a big advantage. At least not in most of the cases.

You can download this short example repo and play with it, make your own judgement and examples.


Conclusion

There are indeed many ways of solving the title issue. I personally go with simple GCD semaphores approach, at least until Promises finally make it into the Swift language in all their beauty and power.