首页 > 解决方案 > iOS:Swinject 创建依赖注入容器层次结构的正确方法

问题描述

我使用了 KooberApp 的示例项目(它是 Raywenderlich 书籍高级架构示例项目),并尝试使用某些框架替换自定义的控制容器反转和依赖注入代码。我认为最流行的 iOS 依赖注入框架是 Swinject。在那里我可以为服务注册组件。我想类似于原始应用程序组件的生命周期。经过一番运行和尝试,应用程序似乎可以正常工作。但我不能 100% 确定我使用的方法是最好的,并且我没有错过重要的事情。我认为我仍然可以与组件生命周期存在一些不一致,即使用范围 .container、.graph、.transient、.weak

我有人可以建议这个 Container 实现是否正确,或者应该修复、更改、修改、做得更好?

应用容器

import Swinject
import SwinjectAutoregistration

public class DIAppContainer {

    public class func get() -> Container {

        let container = Container()

        container.register(Container.self, name: "main") { r in
            return DIMainContainer.get(parent: container)
        }

        container.autoregister(UserSessionCoding.self, initializer: UserSessionPropertyListCoder.init)
        container.autoregister(AuthRemoteAPI.self, initializer: FakeAuthRemoteAPI.init)

        #if USER_SESSION_DATASTORE_FILEBASED
        container.autoregister(UserSessionDataStore.self, initializer: FileUserSessionDataStore.init)
        #else
        container.autoregister(UserSessionDataStore.self, initializer: KeychainUserSessionDataStore.init)
        #endif
        container.autoregister(UserSessionRepository.self, initializer: KooberUserSessionRepository.init)
        container.autoregister(MainViewModel.self, initializer: MainViewModel.init).inObjectScope(.container)

        container.register(LaunchViewModel.self) { r in
            return LaunchViewModel(userSessionRepository: r.resolve(UserSessionRepository.self)!, notSignedInResponder: r.resolve(MainViewModel.self)!, signedInResponder: r.resolve(MainViewModel.self)!)
        }.inObjectScope(.transient)

        container.register(LaunchViewController.self) { r in
            let vc = LaunchViewController(viewModel: r.resolve(LaunchViewModel.self)!)
            return vc
        }

        container.register(MainViewController.self) { r in
            let vc = MainViewController( viewModel: r.resolve(MainViewModel.self)!,
                                        launchViewController: r.resolve(LaunchViewController.self)!,
                                        mainContainer: r.resolve(Container.self, name: "main")! )
            return vc
        }

        return container
    }
}

主容器

public class DIMainContainer {

    public class func get(parent: Container) -> Container {

        let container = Container(parent: parent, defaultObjectScope: .container)

        container.register(Container.self, name: "onboarding") { r in
            return DIOnboardingContainer.get(parent: container)
        }
        container.register(Container.self, name: "signedin") { r in
            return DISignedInContainer.get(parent: container)
        }

        container.autoregister(OnboardingViewModel.self, initializer: OnboardingViewModel.init).inObjectScope(.weak)

        container.register(OnboardingViewController.self) { r in
            return OnboardingViewController(viewModel: r.resolve(OnboardingViewModel.self)!, onboardingContainer: r.resolve(Container.self, name: "onboarding")! )
        }.inObjectScope(.transient)

        container.autoregister(SignedInViewModel.self, initializer: SignedInViewModel.init).inObjectScope(.weak)
        container.register(SignedInViewController.self) { (r : Resolver, userSession : UserSession) in
            return SignedInViewController(viewModel: r.resolve(SignedInViewModel.self)!, userSession: userSession, signedinContainer: r.resolve(Container.self, name: "signedin")!)
        }.inObjectScope(.transient)

        return container
    }
}

登录容器

public class DISignedInContainer {

    public class func get(parent: Container) -> Container {

        let container = Container(parent: parent)

        container.register(Container.self, name: "pickmeup") { r in
            return DIPickMeUpContainer.get(parent: container)
        }

        //container.autoregister(SignedInViewModel.self, initializer: SignedInViewModel.init)
        container.autoregister(ImageCache.self, initializer: InBundleImageCache.init)
        container.autoregister(Locator.self, initializer: FakeLocator.init)

        // Getting Users Location
        container.register(DeterminedPickUpLocationResponder.self) { r in
            return r.resolve(SignedInViewModel.self)!
        }
        container.register(GettingUsersLocationViewModel.self) { r in
            return GettingUsersLocationViewModel(determinedPickUpLocationResponder: r.resolve(DeterminedPickUpLocationResponder.self)!, locator: r.resolve(Locator.self)!)
        }
        container.register(GettingUsersLocationViewController.self) { r in
            return GettingUsersLocationViewController(viewModel: r.resolve(GettingUsersLocationViewModel.self)!)
        }

        // Pick Me Up
        container.register(PickMeUpViewController.self) { (r: Resolver, location: Location) in

            return PickMeUpViewController(location: location, pickMeUpContainer: r.resolve(Container.self, name: "pickmeup")! )
        }

        // Waiting For Pickup
        container.register(WaitingForPickupViewModel.self) { r in
            return WaitingForPickupViewModel(goToNewRideNavigator: r.resolve(SignedInViewModel.self)!)
        }
        container.register(WaitingForPickupViewController.self) { r in

            return WaitingForPickupViewController(viewModel: r.resolve(WaitingForPickupViewModel.self)!)
        }

        // Profile
        container.register(NotSignedInResponder.self) { r in
            return r.resolve(MainViewModel.self)!
        }
        container.register(DoneWithProfileResponder.self) { r in
            return r.resolve(SignedInViewModel.self)!
        }

        container.register(ProfileViewModel.self) {  (r: Resolver, userSession: UserSession) in
            return ProfileViewModel(userSession: userSession, notSignedInResponder: r.resolve(NotSignedInResponder.self)!, doneWithProfileResponder: r.resolve(DoneWithProfileResponder.self)!, userSessionRepository: r.resolve(UserSessionRepository.self)!)
        }
        container.register(ProfileContentViewController.self) { (r: Resolver, userSession: UserSession) in
            return ProfileContentViewController(viewModel: r.resolve(ProfileViewModel.self, argument: userSession)!)
        }
        container.register(ProfileViewController.self) { (r: Resolver, userSession: UserSession) in
            return ProfileViewController(contentViewController: r.resolve(ProfileContentViewController.self, argument: userSession)!)
        }

        return container
    }
}

入职容器

public class DIOnboardingContainer {

    public class func get(parent: Container) -> Container {

        let container = Container(parent: parent)

        container.register(GoToSignUpNavigator.self) { r in
            return r.resolve(OnboardingViewModel.self)!
        }
        container.register(GoToSignInNavigator.self) { r in
            return r.resolve(OnboardingViewModel.self)!
        }

        container.autoregister(WelcomeViewModel.self, initializer: WelcomeViewModel.init).inObjectScope(.transient)
        container.autoregister(WelcomeViewController.self, initializer: WelcomeViewController.init).inObjectScope(.weak)

        container.register(SignedInResponder.self) { r in
            return r.resolve(MainViewModel.self)!
        }

        container.register(SignInViewModel.self) { r in
            return SignInViewModel(userSessionRepository: r.resolve(UserSessionRepository.self)!, signedInResponder: r.resolve(SignedInResponder.self)!)
        }.inObjectScope(.transient)
        container.autoregister(SignInViewController.self, initializer: SignInViewController.init).inObjectScope(.transient)

        container.register(SignUpViewModel.self) { r in
            return SignUpViewModel(userSessionRepository: r.resolve(UserSessionRepository.self)!, signedInResponder: r.resolve(SignedInResponder.self)!)
        }.inObjectScope(.transient)
        container.autoregister(SignUpViewController.self, initializer: SignUpViewController.init)

        return container 
    }
}

这里 App Container 是在 AppDelegate 中创建的,子容器在父容器中注册并 init 注入到视图控制器中,然后存储在属性中并用于初始化这个子视图控制器

let container : Container = DIAppContainer.get()

这是注入子容器(Main-Scoped)的 MainViewController 示例

  // MARK: - Main-Scoped Container
  let mainContainer: Container

  // MARK: - Properties
  // View Model
  let viewModel: MainViewModel

  // Child View Controllers
  let launchViewController: LaunchViewController
  var signedInViewController: SignedInViewController?
  var onboardingViewController: OnboardingViewController?

  // State
  let disposeBag = DisposeBag()

    // MARK: - Methods
    public init(viewModel: MainViewModel,
               launchViewController: LaunchViewController,
               mainContainer: Container) {
        self.viewModel = viewModel
        self.launchViewController = launchViewController
        self.mainContainer = mainContainer
        super.init()
    }

标签: iosdependency-injectioninversion-of-controlswinject

解决方案


推荐阅读