Dependency Injection using Storyboards
Difficulty: Beginner | Easy | Normal | Challenging
This article has been developed using Xcode 12.0, and Swift 5.3
Updated for Xcode 12.4, and 5.3.2
- You will be expected to be aware how to make a [Single View Application](https://medium.com/swlh/your-first-ios-application-using-xcode-9983cf6efb71
- You should be able to handle segues, but I’m sure you can do that already
- You should be aware of, or have some interest in Dependency Injection)
High level modules should not be dependent on low-level modules, but rather abstractions. If this is true, we can swap out classes rather than making a fixed dependency for a concrete class.
Why it matters
If you are using a network service (say to make those GET requests) to test screens that depend on data from the network service you will need to wait for the request to be made. Now not only is that rather boring to wait for (in larger projects this will take a great deal of time), but is seen as unprofessional as if your network goes down your tests will fail — but the problem actually isn’t your code so the test isn’t performing it’s job!
The final conceptual idea might look like the following:
although the whole process is covered in my article about Dependency Injection)
iOS 13 and above
Whacking a Master-Detail couplet into a storyboard is something that most iOS developers are familiar with:
I gave the segue the attractive name traverseSegue, which moves
this should allow us to nicely traverse from the amazingly named
DetailViewController. It's an awesome. But wait, I want to have a property that is set in the initializer of
DetailViewController! It turns out that this isn't a real problem - we can create an initializer that requires this item to be added
Now to use this requires a touch-of-magic. We go to the storyboard and right-click on the segue and we can see where we can hook up a custom instantiation!
So we can do just that — in the
This is the
IBSegueAction that can be connected to the
Storyboard - which is an awesome!
Note that the system always looks for the segue action in the source of the presentation.
iOS 12 and below
Segue You’ll need to change
note that we’ve used a rather annoying. This can be rather annoying, and involves an optional (which is not optional as it needs to be set when we instantiate the
Instantiation Instead of using a segue, we can instead instantiate a view controller (raise it from the
This pretty much has the same disadvantages as the
segue described above for iOS12. Hmm.
There must be a better way.
Using a Factory We can set up a factory method for the property initialization, this lives inside an abstract factory.
which means that our
InformationViewController can be rather basic and force-unwrap the
item since we are guarenteed that it is avaliable!
This doesn’t provide us with a solution for using
segues but...still...there you go.
Of course we can do even better — This should conform to a protocol that would enhance testing possibilities, as would allowing us to use any storyboard:
Now testing this will involve injecting a Mock factory which might look something like the following:
and an example test (just an example of what you might do, I’m not usually in the habit of raising a View Controller from a test…)
YesL this does involve setting a property directly on the view controller and even an extra function in there. That means the
View Controller looks like the following:
That all seems fine. But what about the initial view controller? The solutions above are rather unsatisfactory.
I mean, the initial view controller should be able to use both a view model and be instantiated without errors or hacks!
Well, this is more than possible.
We do, however, need to make sure that we create that first ViewController in code.
Remove the Storyboard
Info.plist in the project navigator, and then delete the two references to the main storyboard.
We also need to remove the main interface from the project target:
Add an identifier
We are going to ask the storyboard to instantiate a ViewController, but of course it will need to know which viewcontroller to instantiate. To do that, we go to the storyboard, select the view controller and give it a name. I’ve chosen the id “first” that we will later reference.
Update the scene delegate
We can repace the first function in the SceneDelegate with the following:
Now we can start our app, instantiating our viewcontroller and viewmodel correctly — and then instantiate further view models and view controllers and described in this article.
This has been a rather long article, and hopefully has been some help to you. Not many articles cover Testing so hopefully this offers some value for you!
Dependency injection is something that is important when you want to test your project, and that is something which you should always be doing (even if the tests are rather basic in the beginning, you should spend some time making sure your code is up to scratch!).
The implementation of the this article is avaliable at the repo, and I would recommend you download that if you wish to have a working project with this code to help you step through the work.
In any case, I hope this article has helped you out in your project, learning journey or even just satisfied your curiosity. Thank you for reading.
If you’ve any questions, comments or suggestions please hit me up on Twitter