How to remove storyboards from your project

tl;dr I don’t like using storyboards in bigger projects (more than 4-5 screens). When I start a new project from scratch I ussualy do the steps from this tutorial.

I won’t talk about the issues that storyboards have, but I will recommend this post for anyone interested in this topic.

In this tutorial we are going to remove the storyboard from the Single View Application template, setup a navigation controller and present new screens modaly or by pushing them on top of the navigation stack.

Create a new project

Create a new project and select the Single View Application template. Name the project NoStoryboards and select swift as the programming language.

Remove the storyboard

In the project navigator right click Main.storyboard then select delete(or select and press delete) and move to trash.

In the project navigator select your project file.

A list of options should display now. Go to the Deployment Info category and remove the Main Interface.

Optional: Remove the Launch Screen

Remove the LaunchScreen.xib file. In the project options remove the launch screen.

Create the interface file for the main controller

Right click on the projects main group and select New File…. Select the User Interface category and the View template. Name it ViewController.

Open ViewController.xib. Select the File's Owner placeholder. Open the Identity Inspector and change the class to ViewController.

Open the Connections Inspector and connect the view outlet.

Create a new Window

Open AppDelegate.swift and create a new window.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        window = UIWindow(frame: UIScreen.mainScreen().bounds)

        window?.makeKeyAndVisible()
        return true
    }

    ...
}

Load the main controller

Create an instance of ViewController and set it as the root view controller of the window.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        window = UIWindow(frame: UIScreen.mainScreen().bounds)

        var mainViewController = ViewController(nibName: "ViewController", bundle: nil)

        window?.rootViewController = mainViewController
        window?.makeKeyAndVisible()
        return true
    }

    ...
}

Modal Controller

Right click on the projects main group and select New File…. Select the Cocoa Touch Class template.

Name the class ModalController and set the subclass to UIViewController.

Presenting the modal

Open ViewController.xib and add a button from the Components Library. Set the button’s title to Modal.

Write a method that creates and presents a ModalController.

class ViewController: UIViewController {
    @IBAction func didTapModal() {
        var modalController = ModalController(nibName: "ModalController", bundle: nil)

        presentViewController(modalController, animated: true, completion: nil)
    }
}

In ModalController write the dismiss method.

class ModalController: UIViewController {
    @IBAction func dismiss() {
        dismissViewControllerAnimated(true, completion: nil)
    }
}

Open ModalController.xib and add a button from the components library. Set the button title to Dismiss. Connect the Touch Up Inside event with the dismiss action.

Setup Navigation

Inside applicationDidFinishLaunchingWithOptions create a UINavigationController with mainController as root view controller. Make the navigation controller the root view controller of the window.

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        window = UIWindow(frame: UIScreen.mainScreen().bounds)

        var mainViewController = ViewController(nibName: "ViewController", bundle: nil)

        var navigationController = UINavigationController(rootViewController: mainViewController)

        window?.rootViewController = navigationController
        window?.makeKeyAndVisible()

        return true
    }

    ...
}

If you run the project now you will see an empty navigation bar on the screen.

Override the viewDidLoad method and change the title of the screen to Main.

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "Main"
    }

    ...
}

Now the bar should display a title.

Secondary Controller

Create a new view controller called SecondaryController using the Cocoa Touch Class template.

Open ViewController.xib and add a button from the components library. Set the button title to Next.

In ViewController write a method that creates a SecondaryController and then pushes it on the navigation stack.

class ViewController: UIViewController {
    ...

    @IBAction func didTapNext() {
        var secondaryViewController = SecondaryViewController(nibName: "SecondaryController", bundle: nil)

        navigationController?.pushViewController(secondaryViewController, animated: true)
    }
}

In Interface Builder connect the Next button to the didTapNext action.

Add a button called Next in SecondaryController that has the same behaviour as the previous one.

class SecondaryViewController: UIViewController {
    ...

    @IBAction func didTapNext() {
        var secondaryViewController = SecondaryViewController(nibName: "SecondaryController", bundle: nil)

        navigationController?.pushViewController(secondaryViewController, animated: true)
    }
}

Implement a method called didTapBack that pops the current view controller from the navigation stack.

class SecondaryViewController: UIViewController {
    ...

    @IBAction func didTapBack() {
        navigationController?.popViewControllerAnimated(true)
    }
}

Add a button called Back in SecondaryController and connect it to the didTapBack action.

Add a property named level to SecondaryController. We are going to use this property to name the secondary controllers.

class SecondaryViewController: UIViewController {
    var level = 1

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "Secondary \(level)"
    }

    ...
}

Change the didTapNext method.

class SecondaryViewController: UIViewController {
    @IBAction func didTapNext() {
        var secondaryViewController = SecondaryViewController(nibName: "SecondaryController", bundle: nil)

        secondaryViewController.level = level + 1

        navigationController?.pushViewController(secondaryViewController, animated: true)
    }

    ...
}

Now we can push an infinite number of secondary controller :)

Conclusions

You don’t have to use storyboards if you don’t like them. It takes about 5 minutes to remove them from your project.

If you want to learn more about View Controllers and Navigation Controllers you should read the View Controller Programming Guide for iOS.

  12 comments for “How to remove storyboards from your project

  1. November 5, 2014 at 9:29 pm

    Why you don’t use storyboards? what’s the point?

    • November 6, 2014 at 6:54 am

      Some like chocolate, some like vanilla. It’s just life :)

    • Daniel
      November 10, 2014 at 1:04 am

      I like storyboards, but in some projects with a complex navigation, the connections are awful.

      Also, if you have too much views controllers, and You works in a old Mac, load all the views controllers in a single Storyboard file is too slow.

  2. Bryce
    November 13, 2014 at 9:29 pm

    I’ve seen this in many projects that I’ve taken over. Solution is to actually use multiple Storyboards. One for each functional domain of the app. Then you can put a category on UIStoryboard to load the appropriate storyboard that you want to use (storyboardWithName: is sort of ugly to see all over the place), so it would be something like [[UIStoryboard domainStoryboard] instantiateInitialViewController]. If a segue doesn’t suit your needs then don’t use one. didSelectRowAtIndexPath still works fine. Another trick is to name your storyboard identifier the same as your view controller class, then introspection will clearly work.

    It’s a nice alternative to folders with a bunch of NIBs in them, and can give you some sense of flow of the app with the layout.

  3. December 4, 2014 at 12:01 pm

    I never know if I start a project with or without the storyboard. I like both methods and both methods have their interests as disadvantages. What criteria do you choose?

    • December 4, 2014 at 2:52 pm

      I’ve started using storyboards in almost all my projects. I think I would say no to storyboards only in projects that don’t target iOS8.

  4. December 9, 2014 at 9:46 pm

    Storyboards are horrible when working in a larger team – the git conflicts are plain awful.
    Also, autolayout is starting to become that bad side effect of having storyboards :)

    • December 10, 2014 at 6:47 pm

      The team at apple made a lot of improvement to Storyboards and xibs since iOS 7 – they are diffable – if that’s a word..
      Also Autolayout improved a lot since it appeared. Since iOS 8 I’ve started using it with joy :)

      • Zivelli
        February 28, 2015 at 3:42 am

        Since IOS 5, 6 & 7 I’ve always programmatically created my views (maybe thats because I read Erica Sadun’s books & picked up my coding style there).

        But with AutoLayout, Swift, multiple screen sizes and yearly ios updates I’m started to wonder whether I should just follow Apple’s guidelines & use storyboards. I’ve also noticed how hard this gets when the client wants a custom UI.

        Are Storyboards the way to go for large & complex Apps with swift?

        • February 28, 2015 at 11:26 am

          Yes, storyboards are the way to go. If the project is to big – split into more storyboars. Or at least use a nib for each view controller. Writing code to generate the UI is generally a bad idea.

  5. Serge
    July 28, 2015 at 3:23 pm

    doing exact you’ve wrote and won’t push secondaryController.

    error: 015-07-28 18:21:13.890 no_storyboard[1396:25818] pushViewController:animated: called on while an existing transition or presentation is occurring; the navigation stack will not be updated.
    2015-07-28 18:21:14.347 no_storyboard[1396:25818] Warning: Attempt to present on which is already presenting (null)

  6. GTennis
    February 14, 2016 at 2:39 pm

    Both are a matter of taste. However, these are the reasons why I prefer xibs over storyboards:

    1. Storyboards break good programming paradigms by disabling you from calling needed designated initializers. For example, let say a screen has subscribed and is observing for some state change (realtime socket communication or other). Then some data arrives in callback, and it needs to be passed as params to a new view controller. With storyboards, we are forced to create a separate property for storing received data, then performSegue, then grab stored data and pass to new view controller… That sucks. Instead, with xib approach, you just pass received data via custom designated initializer to the new view controller, and that’s it. No need to putting received data somewhere in temporary property.

    2. With storyboards there are no construct time dependency injection (only via properties). Anyone who is writing unit tests knows construct time dependency injection is better that property injection because of less bugs.

    3. Storyboards break MVC pattern. According to MVC, every screen has to consist of 3 parts: Controller (ViewController), Model (…), and a View (Xib). Storyboards with multiple Views clearly violate this pattern. This means several devs might work on the same file, and that could lead to unneeded merge conflicts.

    4. There’s no big benefit in capturing app flow in one/several storyboard files. If a person is smart enough to write code then he’s probably smart enough to have a clean modular project structure, and understand app flow implicitly from project structure. Because as soon as app gets bigger, storyboards become messier and provide less immediate flow.

    5. It’s impossible to do navigating part just with storyboards for non-trivial apps. You will still need to code some custom transitioning (custom segues). Also, sometimes you need to open some screens by instantiating them directly from code. This means, navigation flow inevitably becomes a mix of storyboards and a code, and that makes storyboard to lose their primary purpose – to show app navigation flow. That only works for simple <10 screens apps.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)