Simplifying iOS Navigation

Min Kim
Grailed Engineering
3 min readApr 29, 2019

Introduction

Implementing practical architecture is incredibly important. Across all of engineering at Grailed, we try to make it easier to iterate and introduce features by using view models as functions, utilizing higher order components, among many others.

A consistent pain point in our app has been navigation. Many interesting patterns have been developed to solve this problem, most popular being the coordinator pattern. It helps abstract coordination logic from view controllers and provides flexibility in implementing complex flows. There is, as always, some associated cost to abstraction. Coordinators can lead to a lot of boilerplate and sometimes make it cumbersome to implement simple flows of passing data back and forth.

Bearing in mind that navigation is no different than any other asynchronous task, it would be rather nice to implement a solution that allows us to treat navigation as any other async function. At Grailed, we have been using the following approach:

Simplified Navigation

Building off of DJ’s example of a log in screen with view models as functions, let’s add an additional country field. Instead of adding a text field to our screen, we decide to create a new screen to better present all 193 countries.

Before we create the controller, we can gain a lot of insight into how this controller can be architected by considering the inputs and outputs. In this case, we input the list of countries and it outputs one of those countries as selected by the user.

We can then represent this function as a View Controller

The selector receives an array of countries through its initializer to create a beautiful UI representing all 193 countries. Just like our function, the user selects a country which is then outputted by didSelectCountry and, conveniently, dismisses the controller.

The LoginViewController could handle this pseudo-function like so:

Or with some convenient functional operators

To incorporate this pattern into our view model as a function, lets augment DJ’s loginViewModel with a new input of countryFieldTapped and didSelectCountry and output of presentCountrySelector. When the user taps the country field, we want to present them with the country selector which will asynchronously return data back to the view model and eventually enable the login button when valid.

Which we can implement in the LoginViewController

The CountrySelector is completely modular and functionally an asynchronous function. This pattern has allowed us to, rather trivially, create controllers that can asynchronously return data! You can even imagine a generic selector that can take and return any data type, which we may demonstrate in a future blog post.

Even better, notice how simple it was to add functionality to an existing screen using our view models as functions!

If you have any good suggestions on what to name this pattern, you can input them here.

Wrap Up

There are so many great blog posts and videos on the immense number of architectures and patterns. Instead of being dogmatic, it is important to consider trade-offs and focus on consistency. We have found this pattern to work for us and we look forward to hearing your feedback! Our Android counterpart has a similar implementation with interesting differences that highlight ways in which navigation differ between the platforms, so look out for that blog post as well.

If you found any of this interesting, you might also consider working with us. Grailed is based in NYC and we are hiring! Also if you want to talk to me, you can find me on Twitter.

I have added this pattern to our sample project.

--

--