iOS: View Controller Data Injection with Storyboards and Segues in Swift

Since I openly declared my love for Storyboards & Nibs earlier this week, I’ve gotten a lot of questions on how I deal with injecting dependencies between View Controllers without using a custom initializer. So I’ll share that now.

But first, I’d like to say that my solution is not perfect, and it’s a workaround around how UIViewController works. Hopefully Apple will provide a more flexible and Swifty UIKit API (with protocols instead of subclassing!) down the line that will allow the very common case of dependency injection!

Second, the reason I’m a against messing with the initializers is because we have to subclass our UIViewControllers from UIKit. There are already several initializers there and we have to call super on them because UIKit is doing work behind the scenes (which we cannot see the source code of):

Sure, it’s been the same for while and will likely not change, but since I don’t have control or visibility into UIKit, I prefer to work with how it’s meant to be used rather than against it by creating my own clever initializers!

So the way I prefer to handle this issue is by using Implicitly Unwrapped Optionals 😱😱😱

I’ll use this project as an example. The detail view controller – the one that depends on a value from the previous view controller, has an implicitly unwrapped optional variable for the value that it absolutely needs at the time that it’s initialized or loaded:

In this case, if mainText is NOT assigned in prepareForSegue, this view controller will immediately crash 💥 This will make it super easy for the developer writing this code to immediately address the problem and assign the dependency:

The key is to make sure that the implicitly unused optional is assigned or used during the detail view controller’s initialization / view loading cycle! If it’s used later on, the view controller will not crash when the implicitly unwrapped optional is passed in as @justMaku points out:

As I mentioned, this is not the perfect solution, just one I’m ok living with compared to alternatives giving the constraints of UIKit.

You can view the full sample project on Github here.

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.

  • devnikor

    I use the same approach (IUO). But I have a view controller coordinator (with the right initializer) for every controller and this is it’s responsibility to initialize all of the implicitly unwrapped optionals. But I don’t use segues

  • Abdullah

    why we don’t use this instead: mainLabel.text = mainText ?? “check prepareForSegue”

    • Without this dependency, the View Controller would not work at all (most of the time, I use this pattern to inject a dataSource for a tableView for example). It’s just not an optional and crashing it makes that clear and obvious.

  • Marin Todorov

    I’m using a static method on the view controller that binds storyboard + inbound data + any injections I need to do, e.g. here:

    • Bruno Bilescky

      Cool, but then you cant use the storyboard segue, and need to deal with navigation inside your class, losing 50% of the goods from storyboard.
      But if you are willing to lose segues, your code is one of the best i’ve seen about it

      • Marin Todorov

        For me it’s more important to do dependency injection properly so what I do quite often is to lay down my view controllers in a storyboard and connect them with generic segues that aren’t connected to any elements just to give me visual feedback of the use flows. Then I actually manage navigation from code

        • George

          I used the same approach! Glad I’m not the only one 🙂 Thanks for sharing your solution, it’s a brilliant idea!

    • Alexandre Goloskok

      Great idea! Updated for Swift 4 and simplified a little:

  • Apokrupto

    While this solution uses IUO and makes people uncomfortable, the fact is it’s also following the pattern already laid out by Apple when setting outlets, so it’s not exactly heresy to be consistent. That said, the fact that the var can be set and reset is troubling.

    I use this solution myself, but I would love something that treats my var as read only as soon as it’s set, some sort of way to allow clients to implement the lazy setter, so that it can be done only once.

  • あめほし

    I am using the factory pattern to hide the view controller`s IUO interface. if you have one or more init func in the vc, is easy to just add the vc init method in the factory.

  • Pablo
  • Marco Pappalardo

    Just Assert every variable that has to be injected in the viewDidLoad to avoid forgetting any (solution for bug: reallyImportantStringForFeatureX)

  • Tristan Burnside

    Cool, I ended up having to do this in a framework I am working on too.

    I’d be really interested in a post showing what you think DI in a “Swifty UIKit API (with protocols instead of subclassing!)” would (or should) look like.

  • Ian Terrell

    I’ve generalized this approach slightly further by 1) creating another enum to handle the casting of the destination view controllers, and 2) autogenerating the enums from the storyboard. You can see it here:

    Usage then looks like this:

  • Fred Adda

    I feel comfortable with your solution. It’s not crazy to think that public, mandatory properties should be represented as an implicitely unwrapped optional.
    And having the program crash during tests because IUO were nil helped me solve a lot of bugs preventively.