Swinject Version: 1.1.2
Xcode Version: Version 7.3.1 (7D1014)
ViewController in a UITabbarControllerSecond.storyboardSecondViewControllerUITabbarController in Main.storyboard and the SecondViewController in Second.storyboard@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let container = SwinjectStoryboard.defaultContainer
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Configure Swinject
container.registerForStoryboard(CustomTabbarController.self) {
r, vc in
print("registerForStoryboard: CustomTabbarController")
vc.viewModel = r.resolve(TabbarViewModel.self)
}
container.register(TabbarViewModel.self) {
r in
print("register: TabbarViewModel")
return TabbarViewModel()
}
container.registerForStoryboard(FirstViewController.self) {
r, vc in
print("registerForStoryboard: FirstViewController")
vc.viewModel = r.resolve(FirstViewModel.self)
}
container.register(FirstViewModel.self) {
r in
print("register: FirstViewModel")
return FirstViewModel()
}
container.registerForStoryboard(SecondViewController.self) {
r, vc in
print("registerForStoryboard: SecondViewController")
vc.viewModel = r.resolve(SecondViewModel.self)
}
container.register(SecondViewModel.self) {
r in
print("register: SecondViewModel")
return SecondViewModel()
}
setupInitialView()
return true
}
private func setupInitialView() {
// We need to setup our view und rootViewController manually, so we
// can use the SwinjectStoryboard for our dependency resolving mechanism
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.makeKeyAndVisible()
self.window = window
let storyboard = SwinjectStoryboard.create(name: "Main", bundle: nil, container: container)
window.rootViewController = storyboard.instantiateInitialViewController()
}
}
This will produce following log
registerForStoryboard: SecondViewController
register: SecondViewModel
Init SecondViewModel
registerForStoryboard: CustomTabbarController
register: TabbarViewModel
Init TabBarViewModel
registerForStoryboard: FirstViewController
register: FirstViewModel
Init FirstViewModel
registerForStoryboard: SecondViewController // <---- Second Time
register: SecondViewModel // <---- Second Time
Init SecondViewModel
As you can see, the factories for SecondViewController and SecondViewModel are called twice.
I think this has something to do with container view controllers and it's child view controllers. As I debugged SwinjectStoryboard if found out, that injectDependency calls injectDependency for all child view controllers, but the injection for the child view controllers of the UITabBarController are already done at this point and will be repeated again.
private func injectDependency(viewController: UIViewController) {
let registrationName = viewController.swinjectRegistrationName
// Xcode 7.1 workaround for Issue #10. This workaround is not necessary with Xcode 7.
// The actual controller type is distinguished by the dynamic type name in `nameWithActualType`.
//
// If a future update of Xcode fixes the problem, replace the resolution with the following code and fix registerForStoryboard too:
// container.resolve(controller.dynamicType, argument: controller as Container.Controller, name: registrationName)
let nameWithActualType = String(reflecting: viewController.dynamicType) + ":" + (registrationName ?? "")
container.value.resolve(Container.Controller.self, name: nameWithActualType, argument: viewController as Container.Controller)
// <---- This causes the multiple factory invocation.
for child in viewController.childViewControllers {
injectDependency(child)
}
}
_Main.storyboard_

_Second.storyboard_

@phillippbertram, thank you for submitting the issue馃槂 It looks a bug. I'm not sure it's a bug only in the latest iOS, or in older versions of iOS too, because I think I intentionally added the recursive code in injectDependency.
for child in viewController.childViewControllers {
injectDependency(child)
}
I need some investigation.
@yoichitgy you're welcome :)
did you already investigate this problem and can tell me, when you can fix this?
Thanks a lot
Hi @phillippbertram.
I still didn't investigate the issue. Hopefully I'll be able to investigate the issue by the release of Swinject v2.
Each Storyboard link creates SwinjectStoryboard instance. If controller1 is located in storyboardA and has child controller2 located in storyboardB, controller2 will be resolved 2 times during recursive injection of controller1 children's dependencies and when instantiateViewControllerWithIdentifier for storyboardB will be called
@VladimirBorodko, thank you for investigating the issue馃槂
This issue is fixed by PR #15 in SwinjectStoryboard repo for Swinject v2. It might be good to port the changes to Swinject v1.
Fixed by @jakubvano in pull request #144. It will be available in v1.1.4 soon.
awesome 馃拑 馃憤
thanks a lot