Navigation controllers are indispensable on iOS, tvOS, and, more recently, Mac Catalyst. The UINavigationController class is easy to work with. A navigation controller is an example of view controller containment. Every navigation controller manages a stack of view controllers. View controllers can be pushed onto the navigation stack or popped from the navigation stack.

// Push View Controller Onto Navigation Stack
navigationController.pushViewController(viewController, animated: true)

// Pop View Controller From Navigation Stack
navigationController.popViewController(animated: true)

Accessing the Root View Controller

There are times when you need to access the root view controller of the navigation controller. You can access the top view controller through the topViewController computed property, but the UINavigationController class doesn't offer an API that easily lets you access the root controller. But it's easy to do. The root view controller is simply the view controller that sits at the bottom of the navigation stack.

A navigation controller manages a navigation stack.

You can access the navigation controller's array of view controllers through its viewControllers property. To access the root view controller, we ask for the first item of the array of view controllers. Each view controller in the array is a child view controller and the navigation controller is the parent view controller.

// Access View Controllers of Navigation Controller
navigationController.viewControllers

// Access Root View Controller of Navigation Controller
navigationController.viewControllers.first

Adding a Dash of Elegance With an Extension

Let's make the solution a bit more elegant by creating a simple extension for the UINavigationController class. Add a Swift file to your project, insert an import statement for UIKit at the top, and define an extension for UINavigationController.

import UIKit

extension UINavigationController {

}

We define a read-only computed property, rootViewController, of type UIViewController?. Notice that the type of rootViewController is an optional type. Why that is becomes clear in a moment.

import UIKit

extension UINavigationController {

    var rootViewController: UIViewController? {

    }

}

In the body of the computed property, we ask the array of view controllers for its first element. That explains why rootViewController is of type UIViewController?. The first computed property of Array returns an optional because it is possible that the array is empty.

import UIKit

extension UINavigationController {

    var rootViewController: UIViewController? {
        return viewControllers.first
    }

}

You could access the root view controller by using subscript syntax, but know that it is technically possible that the navigation controller's array of view controllers is empty. If you try to access the view controller at index 0 when the array of view controllers is empty, a runtime exception is throw. This means that your application crashes. Take a look at the below example.

Always safely access the root view controller.

In summary, always access the root view controller through the first computed property. That is the safest option.

Safely access the root view controller.