iOS app navigation synchronization with NavigationQueue

Vladas Drejeris
iToDEV
Published in
6 min readMar 17, 2022
Photo by Kelly Sikkema on Unsplash

Hello, today I wanted to talk with you about mobile app navigation. In one of the projects I worked with, we had a problem. We needed to present multiple screens in a sequence. The application had quick actions, push notifications, deep links, and several popups that may show up on the app start. To solve this challenge, our team came up with an idea to use a queue to perform navigation operations. This approach allowed us to push navigation operations to a separate component that performs navigation in a sequence. This component turned out to be a great tool. Now we are using it in multiple projects. Therefore, I wanted to share our solution with you.

Should we use callbacks and flags?

First, let’s look at a common way to solve this problem and its drawbacks. If you came here just for the goodies, you may go ahead and skip this section.

When we encounter this kind of problem, the first thought that comes to our mind is probably callbacks and flags. Let us explore an example of a similar implementation. Here we have code that handles an app shortcut and presents a marketing popup simultaneously on app start.

It doesn’t look pretty, right? We need to couple two different functionalities to ensure that navigation works correctly. Now, imagine that you may have an error alert popping up from any view controller that was currently on top, and it becomes a nightmare to handle. There has to be a better way.

Photo by AbsolutVision on Unsplash

NavigationQueue to the rescue

Solution overview

As I said before, we came up with an idea to use a queue to do all the heavy lifting of navigation. The architecture of our solution looks like this:

  1. NavigationQueue — the main class is responsible for queueing and executing navigation operations in a sequence.
  2. NavigationOperation — the base class that provides a template and common logic for all navigation operations.
  3. ShowNavigationOperation — a concrete operation class that uses the `show` method to present a view controller.
  4. PresentModalOperation — a navigation operation class that presents a view controller modally.

Next, let’s dig deeper and see how we have implemented each of these four components.

NavigationQueue

NavigationQueue is the main class that handles the queueing and execution of navigation actions. As you can see below, this class is pretty straightforward. iOS already has a great Foundation API for operations and queues — called OperationQueue. Therefore, we decided to implement NavigationQueue as a wrapper for this class.

Here’s what we have done:

  1. We created a standard operation queue that does all the hard work. Configured it to perform one operation at a time because we wanted to run them in a sequence. Additionally, we specified quality of service to user-interactive because operations execute actions associated with UI.
  2. Next, we created a singleton instance of the NavigationQueue. It was needed to ensure that the application used a single queue. Otherwise, we would not have the desired navigation synchronization.
  3. Finally, we wanted to limit what operations can be run by this queue. Therefore, we have not exposed the internal operation queue and wrote a method that allows only the subclasses of NavigationOperation added.

NavigationOperation

Next, we have the NavigationOperation class. There were two design choices for this API component: define it as a protocol or implement it as an abstract class. I usually prefer protocols. Despite that, we chose the abstract class. It fits here better because there are numerous things that we need to override from Foundation.Operation, and the abstract class allows us to do it just once. And yes, I do understand that Swift doesn’t yet have a native abstract class feature, but there is a way to make a pseudo abstract class in Swift. Let’s see how we have implemented it.

  1. First of all, we needed to override and implement some states to make the Operation subclass work. If you have ever used a Foundation.OperationQueue, you should be familiar with them. If not, there are some great tutorials on the internet.
  2. Secondly, we overrode the start method from the Operation class. This method performs the actual work of the operation. We check and update the internal operation state and call the execute to run the logic.
  3. Thirdly, as you may know, operations are executed on background threads, and navigation performs on the main UI thread. Therefore, we made sure to call execute from the main GCD queue. This way, we don’t need to worry about multi-threading inside concrete navigation operation implementations.
  4. Next, we wrote an abstract method that performs the actual navigation logic. We made sure that the subclasses override this method by adding a fatalError.
  5. Finally, we added a method that updates the internal operation state when an operation finishes. Subclasses must call it after the navigation action.

ShowNavigationOperation

To use operation queue in action, we need concrete navigation operations. Let’s start by looking at ShowNavigationOperation.This operation wraps the UIViewController.show(_:sender:) navigation method.

  1. We defined source and destination view controllers involved in navigation action. And an optional sender passed to the show method.
  2. A property specifying the operation duration.
  3. Navigation logic is implemented inside the execute method. It performs the show call.
  4. As you know, the view controller’s show method doesn’t have any completion handler. Therefore, we used a delayed call from GCD API to finish this operation after 0.4 s.

PresentModalOperation

Next, we have the PresentModalOperation, an operation for cases where we specifically want to present a view controller modally. Let’s take a look at the code how we implemented it.

  1. First, we have a modal view controller that will be presented and a presenting view controller that presents it.
  2. Second, we have a state property that specifies if the presentation should be animated.
  3. Third, execute method calls view controller present method and invoke finish in completion closure. This operation is very straightforward.

Bonus! Let’s write some extensions

Photo by Kira auf der Heide on Unsplash

As a bonus, I will give you a couple of extensions that will make it very easy to use the navigation queue inside your code. These extensions act as wrappers and allow you to isolate navigation operations creation.

Putting it all together

Let’s put it all together and see what the example provided at the beginning of this post looks like with NavigationQueue.

As you can see, NavigationQueue has allowed us to simplify this logic. We no longer need any flags to synchronize navigation operations, and we have separated the quick actions and marketing popup handling. Another great thing is those extensions I have previously shown. They allow us to use NavigationQueue without any overhead. We only need to replace show and present methods with their counterparts enqueueShow and enqueuePresent.

Final thoughts

In this article, I have presented you with our team’s approach to navigation synchronization in iOS applications. We have decided to implement a NavigationQueue component that manages navigation operations for us. This approach allows us to separate different app features and is quite agile: it is simple to write various navigation operations. If you find it useful, feel free to use this idea in your projects. The examples I have provided are somewhat simplified and only used as proof of concept. Therefore, they don’t consider the edge cases and are not ready for production code, use them of your own accord.

--

--