adessoTurkey / ios-boilerplate

iOS boilerplate app that showcases architecture and libraries used at adesso Turkey
45 stars 4 forks source link

Dependency Injection Library Analysis #1

Closed adessoTurkey closed 4 years ago

adessoTurkey commented 4 years ago

Possible DI libraries should be examined. A pros/cons list of libraries is required to help to decide which is to be used in boilerplate.

kaanbiryol commented 4 years ago

Introduction

It is possible to easily implement a constructor, method, and property injection manually. We've previously discussed in our meetup that we might pass our dependencies manually. I think it is a clean way of doing it as long as we stick with abstractions but there is one downside I'd like to bring up. The point of using the dependency injection pattern is decreasing the coupling between our objects. However, the moment we start passing dependencies manually, we are increasing the coupling by forcing our dependencies to be constructed inside other dependencies. I suppose composition root is the go-to pattern for this case but I feel like this is something we should be discussing in our next meet up.

Alternatives considered

I have examined a dozen (there are even more that are not on the list. see Swifjection, Weaver, Firebolt, Shank) of DI libraries and after deciding they are all very similar with their API and provide a similar set of features I've decided to remove those who have not received a commit in at least 6 months and have a minimum of 500 stars. Therefore we are left with; Swinject, Cleanse and Dip.

I have examined all of these libraries' documentation and along with some examples. I will not get into the implementation differences of these libraries since, at the core, they all work like a big dependency injection container initialized in AppDelegate. You first create a dependency container in AppDelegate, then you can resolve dependencies with closures like;

container.register(LoggerProtocol.self) { _ in Logger(level: .verbose) }
container.register(NetworkModuleProtocol.self) { module in 
    ConcreteNetworkModule(logger: module.resolve(Logger.self)!)
}

The injection part differs between libraries. Some chose injection via extensions some via dependency containers.

The learning curve for these libs doesn't seem like too much if you are familiar with any other DI framework. (except for Cleanse. its API is too dagger-y. seemed a bit complicated to me. maybe because it is developed by the same team with Dagger :unamused: ).

Almost all of them support constructor, method, and property injections. Most of these libraries contain Scopes (Swinject also has custom-scope support), auto-resolving, circular dependencies, storyboard integrations, named definitions, subcomponents. Some even have their code generators. However, I find it hard to determine the cons of these frameworks without using them.

The two cons I have found common in most of these libraries are;

  1. Dependencies are resolved at runtime so there is no way to see if we are doing something wrong at compile time. (except for Weaver they seem to have partially solved this issue with code generation)
    1. The dependencies are loaded at startup. Maybe we can find a way to register them when needed.

Proposed solution

In my opinion, using one of these libraries would bring unnecessary complexity. Honestly, I don't see how using one of them would improve our codebase or general developer productivity. As far as I see these libraries are just big dependency containers initialized in AppDelegate which might be implemented in our codebase with similar advantages.

My solution is that we try something experimental since we are trying to build a boilerplate from ground zero. If we can agree on that we should be using some sort of dependency container rather than passing dependencies manually, we might try some experimental solutions to this problem with Swift's new feature PropertyWrapper or a protocol-oriented approach like this one.

If we can't come up with something we are satisfied with, we might argue about which one of these frameworks would benefit us more. We'd certainly need much more detailed research about these three libraries and maybe use them in small examples to grasp their pros cons against each other but as I've said before it is hard for me to find the pros cons of these frameworks without actually using them.

akayrak-adesso commented 4 years ago

As a result of the meeting, we have decided to make a deep analysis of PropertyWrapper and the possible advantages of DI methods.

akayrak-adesso commented 4 years ago

We have decided not to use any DI library.