Storyboards are great for building user interfaces, but it can sometimes be frustrating to debug the cryptic warnings and errors Xcode throws at you. In this episode of Swift Help, I show you how to avoid unreachable view controllers when working with storyboards.

I have created a starter project to get us started. You can download the project if you want to follow along with me. The project has two storyboards, Main.storyboard and LaunchScreen.storyboard. Xcode creates these storyboards for us. We are only interested in Main.storyboard.

Main.storyboard and LaunchScreen.storyboard

The project defines two UIViewController subclasses, ViewController and MyViewController. Main.storyboard contains two scenes, View Controller Scene and My View Controller Scene.

One of the view controllers is unreachable.

If we build the project, Xcode warns us that one of the view controllers in Main.storyboard is unreachable. The warning reads My View Controller is unreachable because it has no entry points, and no identifier for runtime access view -[UIStoryboard instantiateViewControllerWithIdentifier:]. The goal of this episode of Swift Help is to dissect this warning and understand what it means. That should teach you how to use storyboards without becoming frustrated.

Main.storyboard contains an unreachable view controller.

View Controller Is Unreachable

What you need to understand before we continue is that a storyboard can only instantiate a view controller if the view controller:

  1. is the initial view controller of the storyboard.
  2. is reachable via a segue.
  3. has as storyboard ID.

If none of these requirements are met, the view controller is unreachable. That is what Xcode warns us about. The My View Controller Scene has no entry points because (1) the my view controller is not the initial view controller of the storyboard and (2) it isn't reachable via a segue. We cannot instantiate it programmatically because the my view controller doesn't have a storyboard ID or storyboard identifier. Let's take a look at each of these issues in detail.

Setting the Initial View Controller of the Storyboard

Open Main.storyboard and select the View Controller Scene. Notice that an arrow is pointing to the view controller in the storyboard. The arrow indicates that view controller is the initial view controller of the storyboard. With the view controller selected, open the Attributes Inspector on the right.

Setting the Initial View Controller of the Storyboard

In the View Controller section, the Is Initial View Controller is selected. Unchecking the checkbox, removes the arrow. Checking the checkbox adds the arrow. We don't want to change the initial view controller of the storyboard, but we have two other options to resolve the warning Xcode outputs.

Creating a Segue

One option to resolve the warning is by connecting the My View Controller Scene with the View Controller Scene via a segue. A segue defines the visual transition between two view controllers. By creating a segue, the view controller is no longer unreachable.

The View Controller Scene contains two buttons, Segue and Code. Select the Segue button, press Control, and drag from the button to the My View Controller Scene. Choose Action Segue > Show from the menu that pops up.

Creating a Segue

Defining a Storyboard ID/Storyboard Identifier

The second option to resolve the warning is by defining the storyboard ID or storyboard identifier of the view controller. Select the My View Controller Scene and open the Identity Inspector on the right. In the Identity section, set Storyboard ID to MyViewController. The storyboard ID can be any string.

Defining a Storyboard ID/Storyboard Identifier

Open ViewController.swift. When the user taps the Code button, the showMyViewController(_:) method is executed. Let's instantiate a MyViewController instance and programmatically present it to the user. We load the main storyboard and ask it to instantiate the view controller with identifier MyViewController. The string we pass to the instantiateViewController(identifier:) method is the string we defined in the storyboard a moment ago. It is the storyboard ID or storyboard identifier.

Notice that we use a guard statement and throw a fatal error if the storyboard isn't able to instantiate the MyViewController instance. Why is that? This operation should never fail. If it fails, it means we made a mistake we need to fix.

With the MyViewController instance instantiated, we ask the view controller to present it to the user by invoking the present(_:animated:completion:) method.

import UIKit

class ViewController: UIViewController {
	
	...

    // MARK: - Actions
    
    @IBAction func showMyViewController(_ sender: Any) {
        // Instantiate My View Controller
        guard let myViewController = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(identifier: "MyViewController") as? MyViewController else {
            fatalError("Unable to Instantiate My View Controller")
        }
        
        // Present My View Controller
        present(myViewController, animated: true)
    }

}

Build and Run

Build and run the application in a simulator or on a physical device. By tapping the Segue button, the segue we defined in Main.storyboard is executed. By tapping the Code button, the showMyViewController(_:) method is executed. Notice that Xcode no longer shows us a warning, that is, the view controller is no longer unreachable. That is another problem solved.