App Coordinator for Adaptive User Interface Design

Leveraging Patterns in Software Design Architecture to Improve User Experience

Ray Migneco
Oct 3, 2019 · 8 min read

Introduction

Over the last several years, the App Coordinator software architecture pattern has gained popularity among developers because it helps manage many of the problems encountered when developing mobile applications. This pattern employs established software design principles, such as single-responsibility and dependency inversion, to help keep code robust and maintainable. More so, writing mobile applications with the App Coordinator pattern often leads to highly reusable software, which greatly facilitates feature development and helps to extend the life of an application’s code base.

In this article, we discuss our recent efforts at URBN to improve the user experience of our iPad applications (Anthropologie, Free People and Urban Outfitters) by utilizing the App Coordinator pattern to develop a novel catalog browsing experience that takes advantage of existing view components. In doing so, we’ll show that by invoking dependency inversion and protocol composition, our new interface requires minimal changes to our existing modules.

What are Coordinators?

Coordinators are objects responsible for implementing flow control within an application. This may include tasks such as navigation, authentication, networking and many more. In a specific case, Coordinators can be viewed as objects that “boss around” view controllers in an iOS application. But the overarching concept is that they promote segregation between your views and keep them focused on specific tasks rather than incurring bloat from implementing required flow control in an application.

A deep-dive on coordinators is beyond the scope of this article, but there are many great resources out there including here, here and here!

At URBN we exploit the coordinator pattern by using them to manage flow control in our applications. The coordinators we use take several forms such as managing a view controller, creating child coordinators and performing one-off tasks, often in combination.

Background

Product category navigation is an essential part of many e-commerce applications. Typically, a customer may be presented with a menu of selectable product categories which they can expand in order to see related child categories. Selecting one of these child categories may then take them to another view showing image tiles for associated products.

We implement the parent-child navigation paradigm with an accordion-style interface as shown below.

Accordion-style Navigation

In our codebase, this view paradigm is implemented in and a truncated summary follows:

Gist for CategoryNavigationViewController & Delegate

displays navigation items (containing product categories and other destinations) in a instance and responds to user input. If a selected cell corresponds to a product category with children, it either expands or collapses the accordion of associated categories as shown above.

But what happens if a selected item does not have any children? Should this view controller show the product tile view controller like in our example above? If so, should it be pushed onto the navigation stack or presented modally? Perhaps the user should be redirected to an entirely different part of the app? Clearly, managing all this business logic goes beyond the view controller’s responsibilities.

Rather than convolute our view code with complex business logic, we have the view controller depend on an abstraction defined by the protocol. This technique is known as dependency inversion and allows us to decouple the view from the business logic in our application. We utilize the App Coordinator concept and define a Flow Coordinator object, which by conforming to our protocol, implements the desired behavior when a navigation item is selected.

Coordinator topology for CategoryNavigationViewController

Limitations of the Current Design

Our view controller and coordinator architecture is well-suited for iOS devices when the view’s horizontal size class is “compact,” as is the case for iPhone devices in portrait mode. However, the design does not scale well when the view’s horizontal size class is “regular,” which includes many iPad devices. Consider the following screen where the view controller is implemented on an iPad.

Accordion style navigation on the iPad

It’s clear this design wastes screen real estate as the cells contain little more than a category name. This deficiency led us to explore other solutions for presenting catalog data in a more efficient and visually pleasing manner.

Developing a Better Experience

In order to improve our browsing experience on the iPad, our engineering and UX design teams worked closely to set forth clear goals for our new design:

The design should be adaptive. On large devices the UX design should appropriately take advantage of available screen real estate in order to provide users with a rich, organic browsing experience. On smaller devices the existing navigation experience should be maintained to keep the UX simple and uncluttered.

The design should reuse components. When possible, we would like to leverage existing components in our app. Not only does this minimize overall development time, but it will help us produce an experience that is familiar and intuitive for users.

The design should introduce minimal changes to existing code. We wish to avoid introducing new bugs and lengthy QA regressions.

Using these goals as our guidelines, we developed a split navigation UX which we call the shown below.

CategoryBrowseViewController — Hybrid Navigation Experience

The design concept retains the familiar accordion design in the left pane but instead of stretching the accordion across the screen for larger devices, we implement in the right pane to display a scrolling list of product tiles. On smaller devices, the new view controller only displays and maintains the existing functionality. Thus, the new design is adaptive for any size device and repurposes existing modules in our app.

Hybrid Category Browse Experience

Now, our iPad users have a browsing experience that couples categories with their associated products without ever leaving the view.

Building the Interface

At first glance, our new view controller strongly resembles UIKit’s . However, we opted to write a custom container view controller for several reasons:

  • More control over the width of the primary and secondary view controllers and the ability to override their trait collections
  • The flexibility to use the view anywhere in the navigation stack (Apple recommends using as a root view controller)

is initialized with two child view controllers:

  • (primary) is required
  • (secondary) is optional since there may not be associated products for the category

The property determines the child view controller layout and is based on the view controller’s horizontal size class- if it’s “compact”, only the primary view controller is shown. If it’s “regular”, both primary and secondary view controllers are shown.

If the view controller view is expanded, the secondary view controller can be manipulated using the function, which removes the existing instance and replaces it. This allows a user to switch between categories on the same view.

More functionality, more responsibility?

In our new view controller’s implementation, there is no mechanism that allows the child view controllers to directly message each other. So how does the view controller know what to do when a navigation item is selected? What should it do if a product tile is selected or search, sort and filter functions are activated?

Again, we run into an issue of deciding where this business logic should live. At first, we might try to have conform to the protocols that abstract the functionality for both of its child view controllers. These protocols are below:

Category Navigation and Product Browse Protocols

But this is problematic for a few reasons:

  • We want to avoid putting business logic in our view code
  • Reimplementing these would be redundant because our app already handles the existing functionality to route navigation items and implement product browse actions (product selection, search, sort and filter).

The true change in our new design is determining if the new view controller is showing an expanded view and if so, updating the child view controller in response to a navigation item being selected. All other functionality should remain as it were.

Coordinating the View Controllers

In order to implement the new functionality from our design and maintain existing behavior, we implement a new module, the into our app. Its responsibilities are:

  • Maintain an instance of
  • Conform to the protocol
  • Maintain a reference to a object (more on this in a bit)

The gist for this coordinator is below:

CatalogLandingCoordinator.swift

By conforming to , this coordinator functions as a “proxy” between our view controller and our app’s Flow Coordinator by handling navigation item selection as follows:

  • If the view controller is expanded, update the secondary child view controller with a new instance of
  • If the view is collapsed, pass the selection to the so it can decide how to route the navigation item

So what exactly is the ? It's an object that conforms to a new protocol called , which uses Swift protocol composition to combine the interfaces defined by the and protocols:

Category Browse Delegate Gist

This delegate is an important object in our coordinator for a few reasons:

  • It points to an existing object that can handle our desired routing behavior when the view controller’s interface is collapsed
  • It's used to initialize new instances of when the view is expanded, since the delegate points to an existing object that implements that view controller’s interface methods.

The new coordinator gives us the flexibility to handle our application’s business logic in a context-sensitive manner based on the view configuration. Additionally, using protocol composition for our event-forwarding delegate allows us to reuse modules that implement existing functionality thereby avoiding the need to add redundant code to our app.

We can show the relationship between our view modules, interfaces and coordinators in the diagram below:

Catalog Browse Architecture for Views and Coordinators

Conclusion

We’ve shown that the App Coordinator design pattern is a powerful tool for developing new user experiences from existing components. When user-facing view modules depend on abstractions, coordinators can implement business logic in a context-sensitive manner without modifying existing view code. We also show that protocol composition can be used to reroute functionality to existing modules to avoid adding redundant code. Beyond our category browsing experience, we’ve leveraged this approach to improve other features in our apps by reusing existing code and UX designs from our iPhone experience.

URBN Engineering

Powering Urban Outfitters, Inc.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store