Swinject: Cannot inherit from non-open class Assembler. Swift 3

Created on 17 Sep 2016  路  15Comments  路  Source: Swinject/Swinject

Assembler should be marked as open instead of public, otherwise, there is no possibility to subclass it outside.

question

Most helpful comment

Closing the issue, as it has been clarified :wink:

All 15 comments

Thanks @sashagood for pointing out the part.

Actually I was planning to change it to public final class Assembler because it can be used without inheritance as documented here.

In what use cases, do you want to inherit Assembler? Also which methods do you want to override?

I'm trying to use Swinject with VIPER architecture and I have one Assembler class per module. Here you are an example. There are two VIPER modules WeatherList and WeatherDetail. Every module has its Container (AssemblyType) and Assembler.

class WeatherListContainer: AssemblyType {

    func assemble(container: Container) {
        container.register(WeatherDetailAssembler.self) { r in
            let parentAssembler = r.resolve(WeatherListAssembler.self)!
            return WeatherDetailAssembler(parentAssembler: parentAssembler)
        }
        container.register(WeatherListRouterInput.self) { r in
            let router = WeatherListRouter()
            router.detailAssembler = r.resolve(WeatherDetailAssembler.self)
            return router
        }
        container.register(WeatherListInteractorInput.self) { _ in
            WeatherListInteractor()
        }
        container.register(WeatherListViewOutput.self) { (r, view: WeatherListViewInput) in
            var interactor = r.resolve(WeatherListInteractorInput.self)!
            let router = r.resolve(WeatherListRouterInput.self)!
            let presenter = WeatherListPresenter(view: view, interactor: interactor, router: router)
            interactor.output = presenter
            return presenter
        }
        container.registerForStoryboard(WeatherListViewController.self) { (r, c) in
            let presenter = r.resolve(WeatherListViewOutput.self, argument: (c as WeatherListViewInput))!
            c.output = presenter
        }
    }
}
class WeatherListAssembler: Assembler {

    override init(parentAssembler: Assembler?) {
        try! super.init(assemblies: [WeatherListContainer()], parentAssembler: parentAssembler)
    }   
}
class WeatherDetailContainer: AssemblyType {

    func assemble(container: Container) {
        container.register(WeatherDetailInteractorInput.self) { _ in
            WeatherDetailInteractor()
        }
        container.register(WeatherDetailRouterInput.self) { _ in
            WeatherDetailRouter()
        }
        container.register(WeatherDetailViewOutput.self) { (r, view: WeatherDetailViewInput) in
            var interactor = r.resolve(WeatherDetailInteractorInput.self)!
            let router = r.resolve(WeatherDetailRouterInput.self)!
            let presenter = WeatherDetailPresenter(view: view, interactor: interactor, router: router)
            interactor.output = presenter
            return presenter
        }
        container.registerForStoryboard(WeatherDetailViewController.self) { (r, c) in
            let presenter = r.resolve(WeatherDetailViewOutput.self, argument: (c as WeatherDetailViewInput))!
            c.output = presenter
        }
    }
}
class WeatherDetailAssembler: Assembler {

    override init(parentAssembler: Assembler?) {
        try! super.init(assemblies: [WeatherDetailContainer()], parentAssembler: parentAssembler)
    }
}

@yoichitgy I override only init(parentAssembler: Assembler?). Perhaps I'm missing something and there is a better solution how to use Swinject with VIPER architectire. If you can give an advice I will be appreciated.

P.S. Thanks for quick reply

@mowens, @Nikita2k: Do you think Assembler should be open to inherit? Your advice is helpful馃槂 Especially I would like to hear general opinions from @mowens, and opinions from VIPER view from @Nikita2k.

I will read and answer. :) ETA: 2 days

Thank you very much!!! We can wait馃憤

You probably want to keep Assembler final because it's job shouldn't be extendable. As for the VIPER architecture (which I love and use as well!), I create a global Router class that all module router goes through. This class manages the Assembler and can lazy load new AssemblyType instances that bootstrap a VIPER modules into the container via the Assembler. This way you get your module separation plus a nice parent child container relation between your shared managers/services and the VIPER components in each module

@mowens could you please share small example? I'm newbie in VIPER and struggling to imagine the whole picture of your approach

Thanks @mowens very much for your answer馃槂

@sashagood, this might be helpful for you.
https://github.com/Nikita2k/SwiftViper

I also recommend to take a look at SwiftViper repo (this is not a golden standard, but that is what I use).

Personally I prefer composition over inheritance (and I believe Apple helps you a lot with composition). Also you can refer to Design Patterns: Elements of Reusable Object-Oriented Software and https://en.wikipedia.org/wiki/Composition_over_inheritance for short overview.

We also raised similar problem here: https://github.com/Swinject/Swinject/issues/51

As for me, why we can't build custom class that looks like:

class WeatherDetailAssembler {

    private let assembler: Assembler

    init(parentAssembler: Assembler) {
        assembler = try! Assembler(assemblies: [WeatherDetailContainer()], parentAssembler: parentAssembler)
    }

}

Then we could have an assembler available globally, that stores all other custom assemblers.

Does it make sense?

@Nikita2k yes, it makes. I came up with the same approach.

@yoichitgy thanks for the link.

@yoichitgy I think that one is resolved :)

@yoichitgy @Nikita2k @mowens thank you guys for help

Closing the issue, as it has been clarified :wink:

@Nikita2k, thank you for your kind answer! I also prefer composition over inheritance as you told.

We'll change Assembler to final class to clarify.

Was this page helpful?
0 / 5 - 0 ratings