Swinject Framework

What’s is Swinject framework?

"Swinject helps your app split into loosely-coupled components, which can be developed, tested and maintained more easily."

How this can help me with my problems? Let’s see by doing!

First of all, download our sample project, inside the link you will note that we have start project and final project, download and open the start project.

Project link at the beginning: https://github.com/felipeflorencio/SwinjectSampleProject/tree/master/SwinjectSampleProjectStartProject

Swinject repository: https://github.com/Swinject/Swinject

When you have the start project, just go to Podfile file, and uncomment the pod lines and run pod install in order to install all dependencies that we need.

# pod 'Swinject'

# pod 'SwinjectAutoregistration'

# pod 'SwinjectStoryboard'



# target 'SwinjectDemoProjectTests' do

# pod 'Swinject'

# pod 'SwinjectAutoregistration'

# end

After doing this, run you .xcworkspace and let’s start

First Create our Container

The main idea behind using this tool/pattern is to use a container, let’s explain, we have 3 main ideas that we need to implement in order to have all our dependencies managed:

1 — Container;

2 — Register;

3 — Resolver;

With these 3 items, you can easily understand.

Create one class inside Infrastructure folder and create a file called MainContainer, inside this class add this code:

Few things, we created a shared instance “1”, and the reason for this we will see later, but, this is because of the container object idea.

Each time that I want to register all my dependencies, that means, each time that I want to declare in somewhere the objects that I want to use, this place is called “Container”, imagine as a box where I put all my objects/dependencies.

For this we create here a Container “2” and go to the third step, that registers all the dependencies that I want to use later.

Now let’s create a function where we will put all our registrations “3” and call this inside our init() method so we make sure when you call for the first time our shared instance variable “1” you register all our dependencies.

private func setupDefaultContainers()

The registration here is “split” in two pieces, first the “register” and second “resolver”.

Let’s get for example our “BitcoinInteractor” class, for me to use this I need to declare somewhere who is this class, so we register, and I need to instantiate the object soon I found this “register” so I have the resolver.

The process is:

1 — have a container: container.

2 — register inside my container: container.register(

3 — what I’m registering: container.register(BitcoinInteractorProtocol.self , and here, we are using the protocol that this class implements, this is not a requirement, it's just a choice to make use of protocols to decouple even more our class creation.

4 — when I request this instance, I need to resolve, and this is done by the factory parameter:

container.register(PriceFetcher.self, factory: { resolver in ... })

5 — In the factory closure we need to return an instance that fulfills the type that I registered, in this case, it’s my protocol:

container.register(PriceFetcher.self, factory: { resolver in

return BitcoinService(networking: resolver.resolve(Networking.self)!, service: .bitcoin)

})

If you have noticed we receive something inside closure as a parameter, that I named “resolver”, this is actually a “reference” to container resolver, so you can use to “resolve” to get any other dependency that you registered too inside that container.

This is the full implementation, at this moment you “covered” all the basic implementation that we named, we need to have a container, we need to register everything that we want to use and it’s using the resolver from the container that I can get the instance for any object that I registered.

I used this as a sample to explain, but you can look to the other’s that we have and understand the concept.

I recommend for you add a breakpoint when register and inside the closure that is when we resolve, will give to you a nice idea about the difference when both are requested.

The last step for this is to make sure that all our dependencies will be registered, so we need to call this method in somewhere, and the recommended place for this, as it’s our main container is to call inside our app delegate, just add this line of code inside didFinishLaunchingWithOptions method:

// Initialise all our container that we need in our project

_ = MainContainer.sharedContainer

Registering and Resolve our View Controller’s dependencies from Storyboard

We covered the situations of “normal” objects, but for view controllers, we have a different life cycle when coming from a storyboard, and we do not want to have two instances if you initialize your view controller from the storyboard its the storyboard that will handle the view controller instantiation.

For this, we have another pod called SwinjectStoryboard and with this, we can resolve any dependency and set to our view controller and make sure that we still have the same object reference that storyboard created.

Create another file and name this file “SwinjectStoryboardContainer”, and add this implementation:

First, in order to have the storyboard initialization that can be handled by the Swinject tool you need to follow a pattern, that is:

Create the extension: extension SwinjectStoryboard { Add a method inside that will be called automatically: @objc class func setup() Register your implementations using the default container like: defaultContainer.storyboardInitCompleted(

Here you may have noticed that we have the reference to MainContainer container, and this is because we are handling the registration of view controllers inside defaultContainer that is another container, and we can’t find the same item that I registered on another container (Main Container).

As I have a dependency that needs to be fulfilled by one instance that is at another container that is why I chose to have my MainContainer as a singleton that I could use in other places, remember this is personal, you can find other ways of achieving the same thing.

The lesson here is to be able to understand that when I register on another container I need to have a reference to that container to be able to resolve anything that is inside there.

Now we have all our dependencies declared, and properly defined in a way that when the call to be resolved to happen the right instance with the right setup will be made.

Let’s clean our code now!

First, go to our BitcoinViewController class, we don't need anymore to fulfill our dependencies inside our class, remember that we registered this view controller in our extension for the storyboard, and, when this class "resolve" we will inject the dependencies that we need, and in this scenario, we are using property injection.

Go from this:

To this:

You see, way more clean, and two main things that you may have noticed:

1 — In the place that we register our dependencies it’s easier for us to identify all the dependencies that we need, and if need to update or adjust we have one central point.

2 — Applying the concepts of dependency injection our code is cleaner and easier to maintain;

With this easy steps you should be able to run your project and everything will be running as suppose to.