The DI crashers

Let’s crash dependency injections

Jim Lai
8 min readAug 16

--

from imdb

Whatever it takes to get in, get in.

This showed up on highlight.

Efficient Dependency Management in iOS: Introducing AppContainer for MVP+DI architecture

In which he used Swinject, then commented by the creator of Resolver and Factory himself.

It’s a party then. And they didn’t even invite me.

So I decided to crash the party.

This article will contain two parts. First part is code-free. I’m going to quote his motivation story and show you why it’s dog shit.

Second part I’ll be revisiting Factory… again. Since comment mentioned a true container system, I'm going to show you why it’s dog shit too.

I want to try a more essay approach so you can use this as a light reading when taking a shit. The only thing DI is good for, at least in the form they are selling, is to watch industry titans making dumb shit mistakes.

Stop, look, listen. At weddings. In life.

He begins with this motivation story:

Imagine you’re constructing a house. Each room depends on specific items: the kitchen needs an oven, the living room needs a television, and the bathroom requires a shower. You wouldn’t want to walk into another room to fetch these items. Instead, you’d expect everything to be in its rightful place, readily available when needed. In the world of iOS development, we deal with a similar concept: Dependency Injection (DI). By ensuring that every component, or ‘room,’ has its required dependencies, or ‘items,’ we can create a more modular and maintainable architecture.

If you stop and look closely, he just told you why you shouldn’t do injection.

Let me elaborate using his example.

The living room needs a television. You expect it to be in its rightful place, readily available when needed.

What’s the easiest way to do it?

Fix it there.

Instead, to create a “more modular” architecture, every time you want to watch TV, you order one from Amazon, filling out paper work, waiting for delivery, then install it in place.

There is just a cardboard listing TV specs in the place where your TV would be. If your wife wants to watch TV, she has to order one from Amazon per spec, so does everyone else in your family.

And not just TV, no no no. It’s modular we are talking about here. Every fking thing in this living room has to be cardboard too.

You wouldn’t want to walk into another room to fetch these items, right?

But that is exatly what he is suggesting. You have to fetch everything from somewhere outside the living room.

Now, not only do we need a cardboard listing specs for one item. For separation of concerns we want to to use ONE cardboard for ONE spec.

The result is that your living room is full of cardboards and everytime you want to use it you wait for delivery of items that match specs.

The best part of this “dependency” is that you are still depending on something!

Say living room depends on TV. By injection, you are NOT removing that dependency. It may not depend on the TV, but still depends on a TV for it to function.

Does your wife give a shit whether it’s a Sony TV or LG TV? She would care a lot more about the cardboards in the place where TV used to be at.

In this example, you want to REMOVE useless dependencies. I don’t give a shit what TV brand my living room depends. Injecting everything is not skill.

Removing is. Who says you need injection to be modular?

The thing about words like “modular” and “decoupled” is that they can be used to justify anything and it would appear to make sense.

E.g.; create a sink object so they are decoupled, which is injected so they are modular, resulting improved testability.

The most important key word in above sentence is, take a guess?

SINK.

Did I just make sink object look good?

Obviously we can’t call it sink, let’s call it view model. We are now air-tight and ready to sell tutorials.

We haven’t seen any code yet. But from intro you can tell he is about to make some dumb shit mistakes.

The rest of his article is to advertise Swinject, which I’ll not go into, because the creator of Resolver and Factory, who is a top 1000 technical writer on this platform, has already reviewed it. Let’s call this guy… Doug.

Doug commented:

…(omitted)

As that basically devolves this into being a service locator pattern and makes everything dependent on a global singleton.

If you’re doing a true container-based system you probably need to pass the container into the object’s initializer.

A true container-based system? That is way more interesting.

Because I have a fond memory of criticizing the shit out of Factory.

Without any code, let’s speculate on what might happen.

Doug is going to pretend that he is not abusing singleton.

Since Doug is a top 1000 technical writer on this platform, my bet is that he is going design an over-complex injection system for everything that can be easily replaced by simple computed properties.

We have to see some codes now to confirm. If you are taking a shit, I’d suggest you wipe first, and read later.

When it stops being fun, break something

Before looking at Factory, let’s check Resolver for fun.

I pulled this from Resolver github:

public protocol Resolving {
var resolver: Resolver { get }
}

extension Resolving {
public var resolver: Resolver {
return Resolver.root
}
}

Singleton. Obvisouly this is not a true container system.

I personally wouldn’t advise people about not using singleton when I’m the one that use all the singleton. But hey that is just me. I can’t speculate on Doug’s thinking just from out-of-context commment.

Or maybe Resolver is deprecated. In any case let’s see Factory pulled from github.

class ContentViewModel: ObservableObject {
private let myService = Container.shared.myService()
private let eventLogger = Container.shared.eventLogger()
...
}

Singleton. Wait, is this true container system?

Recall that

As that basically devolves this into being a service locator pattern and makes everything dependent on a global singleton.

But that doesn’t apply here? We are not depending on Container.shared in anyway?

OK. I get it. It’s a feature. You can support different paradigms at the same time. So finally let’s inject like a proper true container system.

class ContentViewModel: ObservableObject {
let service: MyServiceType
init(container: Container) {
service = container.service()
}
}

Wait, but why not just let service = MyServiceType() ?

Or you can have a local computed property var service1: MyServiceType {...} if init is non-trivial.

Doug is asking you to create computed property in his container, then inject this container to your shit, then retrive your computed property via container.

He got all the credits for helping you create computed properties. You got zero credits doing all the heavy lifting.

This is why I love Doug. Doug if you are reading this, none but love. I admire his skill to make you write dumb shit, and thank him for it.

The best part is, he knew you might have doubts. So he addressed it first:

If we go back and look at our original view model code one might wonder why we’ve gone to all of this trouble? Why not simply say let myService = MyService() and be done with it?

And he put this under Mock section. Classic bait and switch.

You are writing this for mocking. NOT for production code. Is the mocking so good worth sacrificing code efficientcy of production code?

It’s easy. Just replace MyService with a mock that also conforms to MyServiceType.

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let _ = Container.myService.register { MockService2() }
ContentView()
}
}

This is anything but easy.

Doug used class as variable. 10 mock data you create 10 classes.

Doug used variable as class. myService is a variable representing a class.

This “variable” even has stored property where you can “register”. It’s a computed property mind you.

    var myService: Factory<MyServiceType> { 
self { MyService() }
.scope(.session)
}

If you go through all the heavy lifting of creating MockService2() , guess what?

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
// don't even need MockService2
// just set regular Service object to mock data
let mock = Service(...)
ContentView().environmentObject(mock)
}
}

Remember what we talked about “Removing” earlier?

You can remove container entirely. At what cost? True container system doesn’t use singleton, so they are of limited scope already. Environment object gives you scope to a part of view hierarchy at your discretion.

And we can even remove MockService2() because it’s such a brute force.

In practice, you want a preview based on mock json from fetch. What’s the easiest approach? Switch from production backend to test backend.

Configure base address in network service without exposing it to views.

Then you are testing actual production class not mock class.

This level of mocking isn’t something you should brag about. You should be embarassed having to write this shit.

But these are just soft skills. The hard skill, that truly defines the skill of a dev is how you sell it:

Powerful: Factory supports containers, scopes, passed parameters, contexts, decorators, unit tests, SwiftUI Previews, and much, much more.

Performant: Little to no setup time is needed for the vast majority of your services, resolutions are extremely fast, and no compile-time scripts or build phases are needed.

Safe: Factory is compile-time safe; a factory for a given type must exist or the code simply will not compile.

Concise: Defining a registration usually takes just a single line of code. Same for resolution.

You know why it is “compile-time” safe? Because it is basically a collection of computed properties (that you wrote!). He even took credit for Swift language features.

I know what you are thinking. "You get all that from a comment that contains a word True"?

Yes. If you want to break something because coding isn’t fun anymore, might as well break dependency injection.

If pressed, tell people you are related to uncle John, everyone has an uncle John

This rule needs amend when you crash DI parties.

Tell people you are related to uncle Bob. Everyone knows uncle Bob.

It doesn’t matter if you are writing the dumbest, brut-est force shit, Clean Architecture says there would be light. And there is light.

For me, I’d say a rule handbook for Wedding Crashers is far more useful than Clean Architecture, especially for a dev.

But that is my opinion. Make your own.

If you are still reading because your shit won’t come out, consider joining DI crashers!

Always be a team player. Everyone needs a little help now and again.

--

--

Jim Lai