NEW: Learn SwiftUI with my free YouTube video series! >>

How to use dependency injection with storyboards

Swift version: 5.0

Paul Hudson    @twostraws   

Dependency injection is a fancy name for a simple thing: when we create an object in our app, we want to provide it with all the data it needs to work. Before iOS 13 this wasn’t possible when using storyboards, which meant we ended up with properties that were optional or implicitly unwrapped, even though we knew we’d be setting them immediately.

So, we used to write code like this:

// A view controller that we want to present with some data
class EditUserViewController: UIViewController {
    var selectedUser: User?
    // ...
}

// Our root view controller that wants to create, configure, and present an EditUserViewController
class MainViewController {
    // ...

    func show(user: User) {
        // attempt to load our view controller from the storyboard
        guard let vc = storyboard?.instantiateViewController(withIdentifier: "EditUser") as? EditUserViewController else {
            fatalError("Failed to load EditUserViewController from storyboard.")
        }

        // configure its only property
        vc.selectedUser = user

        // display it
        navigationController?.pushViewController(vc, animated: true)
    }
}

Having optionals in here was unavoidable because we had to let the storyboard handle initializing the view controller, but it adds all sorts of complexity – we can set that value to nil by accident, we can forget to set it at all, and we need to unwrap it as needed.

From iOS 13.0 and later there’s a better solution: a new method on UIStoryboard called instantiateViewController(identifier:creator:), which lets us determine how to create and configure our view controllers.

So, we could rewrite EditUserViewController to this:

class EditUserViewController: UIViewController {
    var selectedUser: User

    init?(coder: NSCoder, selectedUser: User) {
        self.selectedUser = selectedUser
        super.init(coder: coder)
    }

    required init?(coder: NSCoder) {
        fatalError("You must create this view controller with a user.")
    }

    // ...
}

That makes selectedUser non-optional, but also added two custom initializers: one with an NSCoder and a User, and one just with an NSCoder. The second one now uses fatalError() to make it clear that creating an EditUserViewController without a user isn’t allowed.

With that custom initializer in place we can now update MainViewController so that it initializes our EditUserViewController correctly:

func show(user: User) {
    guard let vc = storyboard?.instantiateViewController(identifier: "EditUser", creator: { coder in
        return EditUserViewController(coder: coder, selectedUser: user)
    }) else {
        fatalError("Failed to load EditUserViewController from storyboard.")
    }

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

What’s really changing here is that we’re now handed the NSCoder instance that can create our view controller, and we can use that however we want – including alongside other properties we want to inject. However, it means more places where we can remove optionality from properties, which is always welcome.

LEARN SWIFTUI FOR FREE I wrote a massive, free SwiftUI tutorial collection, and also have a growing list of free SwiftUI tutorials on YouTube – get started today!

Available from iOS 13.0

Did this solution work for you? Please pass it on!

Other people are reading…

About the Swift Knowledge Base

This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.

MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Average rating: 5.0/5

Click here to visit the Hacking with Swift store >>