A few episodes ago we covered dependency injection. We saw that dependencies are things that “speak” to the outside world and because of this are full of side effects, which makes our applications complex. We covered the most popular way of handling dependencies in Swift, which is to put a protocol in front of it, explicitly inject it into initializers of objects that want those dependencies, and then you’re free to plug in a mock version that serves mock data.
We found that this approach has a pretty big problem. It adds a lot of boilerplate. Every dependency added required at least five or six places that needed to be updated, including mocks, initializers, call sites, etc. We proposed a solution that might make some folks feel a little uncomfortable: to reign in all those global singletons and pack them into one single singleton that we called the “current environment”. We saw that this approach cleaned up that boilerplate we saw with the protocol-based approach: new dependencies required very few changes. This approach is also very simple and could be introduced to a code base easily, today, without many changes!
Still, we know how strange this approach can seem at first glance, and we don’t want people to be uncomfortable with it. We want people to see just how simple it is. We’ve used it in our code bases many times.
This episode is just going to be about getting comfortable with it. Let’s relax, step through the app we were building last time, extract out a few more dependencies, and explore how setters can help us.