iosdependency-injectionswinject

Swinject: migrating to assemblies


I'm using awesome Swinject for DI in my project, but I'm using it without assemblies (by the time I've started using Swinject, there was no assemblies).

My current pattern of using Containers is:

class ParentContainer {

    private let container: Container

    init(parentContainer: Container?) {
        container = Container(parent: parentContainer)

        container.register....
    }

    func myTopLevelController() -> MyTopController {
        let controller = container.resolve....
        controller.container = self // controller holds its container
        return controller
    }

    func childContainer() -> ChildContainer {
        return ChildContainer(parentContainer: container)
    }

}


class ChildContainer {

    private let container: Container

    init(parentContainer: Container?) {
    ....
}

With that configuration:

Now I'm trying to apply Assembly. As far as I can understand, my containers now will conform to AssemblyType protocol. But I have some confusion:

  1. Should I resolve instances through assembly, not through container?
  2. What is the purpose of func loaded(resolver: ResolverType) method? should I retain resolver? Can it lead to retain cycle?
  3. I'd like to have TopLevelAssembly, but still use Containers for leaf entitites. Is it possible to pass Assembly as a parent for a container? Or are there any other way to achieve it?

Solution

  • 1. Should I resolve instances through assembly, not through container?

    It's up to you whether you use the assembly feature. It is used to manage grouping of dependencies. Since it looks you already manage the groups of dependencies by ParentContainer and ChildContainer, I think you don't have to use assemblies.

    People who have used Typhoon might prefer the feature. People who have used another register/resolve type DI container might prefer organizing container structures by themself.

    2. What is the purpose of func loaded(resolver: ResolverType) method? should I retain resolver? Can it lead to retain cycle?

    It is called after all assemblies are applied to a container in order to do something that cannot run during assemble method of an Assembly. It is called by the system of Swinject like viewDidLoad of UIViewController called by the UIKit system. The documentation about loaded or a unit test might help you understanding loaded function.

    You shouldn't store the resolver parameter. (Actually I don't see usecases to store it because Assembler doesn't keep references to assemblies, which will be released after you instantiate Assembler. Even if you store it, no retain cycle is caused.)

    3. I'd like to have TopLevelAssembly, but still use Containers for leaf entitites. Is it possible to pass Assembly as a parent for a container? Or are there any other way to achieve it?

    I didn't catch your context about the TopLevelAssembly and leaf entities. I will be able to update my answer later if you add more details.

    Here are some comments to the question parts: You cannot pass an Assembly instance because the initializer of Container is init(parent: Container? = nil), which takes an instance of Container. Another way to achieve it might be just keeping the top level as Container. (Or I have to update Swinject to support the scenario.)

    My answer might not be perfect but I hope it helps you implement the service locator pattern.