Managing Core Navigation through UIWindow

Using windows to separate different modes of your app

Brad Mueller
4 min readApr 14, 2017

Every iOS application has some navigation. Common navigation is done through a UINavigationController to push and pop, a UITabBarController to manage pages, modal controllers or container controllers.

But this is not the kind of navigation I want to talk about.

I want to talk about some “core” navigation.

How do you handle navigation when your user signs in/out? How do you handle navigation if a view needs rare precedence over any other content, like a tutorial, a mask, or a PIN code screen?

A typical approach — Modals

Let’s look at Signing In and Out. A typical way to handle these two modes of an application would be to present authentication as a Modal, on top of your main content.

Unfortunately, using modals has drawbacks:

  • Unable to present a modal upon app launch
    Upon launching, your app will determine if the user is signed in, and if not, you’ll need to show that modal. However, presenting a modal before the initial controller has appeared isn’t possible — the presenting controller must already be in the view hierarchy.
  • Unforeseeable conflicts
    It’s hard to predict the current state of your application when you need to sign a user out. Maybe they signed out explicitly, or because of a 401 network error. If your app already has modal controllers presented (whether your own or any UIAlertController), Sign In must either be presented above the top-most modal, or all modals must be dismissed. Modal transitions already in-progress can also prevent your Sign In presentation.
  • Unintentional side effects
    Dismissing and presenting modals will trigger all sorts of viewWillAppear viewDidDisappear calls throughout your app, which, unless carefully considered, can create some undesirable side-effects.
  • Etc, etc.

It’s messy. And although there are other ways, none are great. So instead, here’s what I do -

The better approach with CFANavigationWindow

Introducing CFANavigationWindow, a class designed to restructure the way core navigation can be managed.

At its core, CFANavigationWindow is just a UIWindow subclass, but it does much more:

  • It exposes methods to easily replace your application’s key window
  • It allows for managing a stack of windows
  • And it provides a means to animate each transition

What does this mean? What are the benefits?

By abstracting custom window management, CFANavigationWindow allows for a code design with clean segmentation between each mode of your application. This means no worrying about different states of the interface, unintended side effects, or trivial limitations.

Whether a user is three modals deep or at your homepage, a window can present above it, beneath it or replace it, without triggering any effects on the existing controllers. The entire navigational hierarchy within a window can persist, unchanged and unknowing about the different mode of the overall application.

Please, consider the following

Assume your application has these core modes:

  • Signed out
  • Signed in
  • Loading (preparing data upon signing in)
  • Locking (presenting subsequent authentication via in-app PIN upon applicationWillEnterForeground:)
  • Masking (covering the signed-in user’s content upon applicationWillResignActive:)
A hierarchical diagram of segmenting the 5 modes into separate windows, as demonstrated in the demo project.

Depending on the state of the application, any one or more of these modes could be active at once (i.e., signed in, loading your content, and asking for your PIN). Further, any mode could become inactive at any time (i.e., loading has finished, while still entering your PIN).

Handling the navigation between these states using Modals would get very complicated, if not impossible. However, with CFANavigationWindow, this can be easily achieved.

Example of window transitions via CFANavigationWindow
  • Transitioning between Signed Out and Signed In can be done by replacing the key window.
  • Transitioning between the default Signed In, Loading, Locking and Masking modes can all be done by managing a stack of windows.

And of course, each mode transition can include a custom animation.

Give me some code

Here are the three main methods used to accomplish all of this. Each is designed to take UIViewController instances as arguments, so all of the window management is done for you.

+ [UIWindow cfa_replaceRootWindowWithController: withAnimator: completion:]- [CFANavigationWindow pushController: atWindowLevel: withAnimator: completion:]- [CFANavigationWindow popController: withAnimator: completion:]

By replacing the root window with a new controller, the existing root window (including its stack of windows) transitions away, and your new controller is presented in a new CFANavigationWindow.

Pushing and popping new windows onto the stack is done at a discrete window level, to ensure that each controller is always shown at its proper priority in the stack. Although it’s designed to have at most 3 windows in the stack, you’re welcome to modify it for more.

Animators, if present, are provided the full context of the transition, including the type of transition and the top-most visible windows, so it knows which windows to animate. If the top-most visible window isn’t changing (i.e., push or pop is happening underneath the visible window), the animator won’t be called, as it’s not needed.

When the animator signals it is finished by calling animationFinished(), all of the managed windows staying on-screen are reset with the proper frame, alpha, transform, etc., and any dismissing windows are properly deallocated.

TL;DR

Handling the “core” navigation of an app can be pretty tricky, and so in my search for a better solution, I built this. I’ve found that, so long as your app isn’t doing some crazy window work already, CFANavigationWindow might just make your life easier.

Please let me know what you think, what other solutions you may use for handling some core navigation, and check out the demo project!

Until next time, get out and smell the sunshine :]

--

--

Brad Mueller

Co-founder & lead mobile dev at a brilliant little place called @Cellaflora