Menu Home

Showing modals as a result for a requests (look at the semaphores)

Last week, I talked to another iOS dev about a classic problem during development that, for sure, if you already participated in a selection process for an iOS mobile dev job, you ended up facing it.

“How to execute a process after a request, one at a time, when your requests run in parallel?”

It was then that the idea for this post came up to my mind.

Let’s see now how we managed to resolve this situation.

GCD – The inglorious saviour

A lot of people fear these letters more than anything when they need to do something asynchronously on iOS.

Why?

If used correctly, Grand Dispatch Central can save you and provide an incredible user experience.

If used incorrectly, it can drive you crazy, with practically “undebugable” errors, almost impossible to reproduce scenarios, memory corruption and chaos.

It is an inglorious saviour: it will get you out of disaster, but he can take you to a much worse one.

The GCD goes far beyond the famous asyncs that devs use to send a process to a background thread or bring processing back to the main thread. It has very useful resources for different types of situations, such as DispatchGroups, DispatchSemaphores, QoS for Global Threads; but today I want to talk a little bit about just one of these features: DispatchSemaphores.

Imagine the situation where a process can be run multiple times, but only one run per time.

It would be simple if we think of a monothread environment. Processing would end, and run again.

#blindPeople: the image shows several processes being executed in a chain, one after the other, in a cycle like “process, screen, process”.

But in a multi-threaded environment, this becomes an issue. How do I know if the processing is finished to start the next one? How do I make processing wait to start the next one, if the previous one is still running?

#blindPeople: the image shows several processes, being directed to a stack, which is directed to the screen.

That’s where queues and semaphores come in.

Queues and Semaphores

First, we need to create a queue in the background to carry out the execution of our process, which in our case will be to create and display a modal in our app.

For this, we created a DispatchQueue. When creating a Queue, we basically have two creation options, based on how the Queue will execute the taks it receives:

Serial: a queue-running execution (FIFO), where one process is performed at time, in the order of entry.

Concurrent: a concurrent execution queue, where each process runs on its own thread, and they run in parallel.

In our case, we’ll create a Serial queue, as our execution will be based on the input order, one at time.

private let dispatchQueue = DispatchQueue(label: "dev.andresalla.DispatchTest.ModalPresenter")

So we solved our problem of opening modals, aren’t we?

WRONG.

As we aren’t in the main thread, we send the modal opening asynchronously to the main thread. After this Dispatch, the execution of our process is ended, and the next one in the queue starts executing.

How will we do it to wait for the next execution for the modal to close? Simple: we’ll use a semaphore.

Semaphores allow you to wait (wait method) until he has free resources to continue.

When you create a semaphore, you specify the amount of “resources” it has, that is, the number of times it can pass through the wait method before it has to wait for some resource to be released (signal method) to proceed with its execution.

In this way, we create our semaphore for allows one resource at time, and ask it to wait in our queue (wait), until the modal is closed.

private let dispatchSemaphore = DispatchSemaphore(value: 1)

But how do we know if the modal has been closed so we can release the resource?

Let’s create a callback!

One callback to rule them all

Our callback will be called in modal, at dismiss, so that we can release the resource of our semaphore.

For this, we created a ModalPresentable Protocol, with a dismissAction variable, which will be executed when the modal is dismissed.

When we put our modal in the queue for presentation, we will ask for it to be in UIViewController and ModalPresentable conformance.

In this way, we can implement the release (signal) of our resource with the semaphore.

protocol ModalPresentable: AnyObject {
    var dismissAction: (() -> Void)? { get set }
}

class ViewController: UIViewController {

    private let dispatchQueue = DispatchQueue(label: "dev.andresalla.DispatchTest.ModalPresenter")
    private let dispatchSemaphore = DispatchSemaphore(value: 1)

    // Outras variáveis e constantes

    func presentModal(_ viewController: UIViewController & ModalPresentable) {
        dispatchQueue.async { [weak self] in
            self?.dispatchSemaphore.wait()
            DispatchQueue.main.async {
                viewController.dismissAction = { [weak self] in
                    self?.dispatchSemaphore.signal()
                }
                self?.present(viewController, animated: true, completion: nil)
            }
        }
    }

    // Outros métodos
}

Run baby, run

And it’s done! Our screens will be displayed sequentially, without being run over, and will be displayed when its predecessor (if any) is closed.

#blindPeople: the image is an animation showing two screens, one purple and one red, being displayed sequentially, one after the other, over a white screen.

I hope this tip is useful for you !! Soon there will be more!

Categories: Tutorials

Tagged as:

André Salla