2

我正在使用Swinject我的 DI 解决方案,并使用扩展对其进行SwinjectStoryboard扩展。

我正在努力动态注入viewModel特定的viewContoller. 具体场景如下:

MyViewController有一个名为 的属性var viewModel: ViewModeling

有 2 种不同的视图模型符合ViewModeling协议,我们称之为:firstViewModelsecondViewModel. 我的故事板只包含一个控制器,它的MyViewController.

问题

注入正确的viewModel作为MyViewController动态的依赖(所以只有在运行时我才会知道是注入第一个还是第二个)

我能够在服务级别上做到这一点(2 个服务符合的一个协议,以及 2 个不同的视图模型,每个视图模型使用不同的服务可以使用特定名称解析所需的一个)

我正在努力在 viewController 级别上执行此操作,尝试将相同的视图控制器注入特定的 viewModel(两者都符合相同的协议)。

目前我的预感是SwinjectStoryboard不允许我使用它的故事板 ID 实例化视图控制器(就像我通常会做的那样),此外还定义了几个将在运行时解析的不同名称。

我错过了什么吗?

4

2 回答 2

2

您没有错过任何东西 - 您正在寻找的行为目前确实无法通过 SwinjectStoryboard 实现。

您可以有多个storyboardInitCompleted不同names的 ,但它们对应于在情节提要参数中输入的名称swinjectRegistrationName(有关更多信息,请参阅文档) - 为了使用它,您需要在情节提要中有多个视图控制器副本。

从我的角度来看,理想的解决方案是注册参数,即您将有一个enum ViewModelType {}将用于storyboardInitCompleted解析正确视图模型的参数。不幸的是,此功能尚未最终确定(请参阅此 PR)。

在当前状态下,我可能会尝试将视图模型的选择从注入逻辑移动到应用程序逻辑 - 即你可以有一些

protocol ViewModelProvider {
    var viewModel: ViewModeling { get }
}

它将被注入视图控制器并根据某些应用程序状态提供正确的视图模型。

绕过这个问题的另一种方法是放弃 SwinjectStoryboard 注册,并使用基本的 Swinject 来实例化视图控制器:

container.register(MyViewController.self, name: "name") {
    let vc = SwinjectStoryboard.create(name: "MyStoryboard", bundle: nil).instantiateViewController(withIdentifier: "identifier")
    vc.viewModel = $0.resolve(ViewModeling.self)
    return viewModel
}

let vc = SwinjectStoryboard.defaultContainer.resolve(MyViewController.self, name: "name")
于 2018-04-10T08:30:54.040 回答
0

ViewModel我的模式,如何注入ViewController

// ------------------------------------------------------------------------------------------
// Option 1, when you use Storyboards
// ------------------------------------------------------------------------------------------

import Foundation

class MyViewController {

    var viewModel: MyViewModel!

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var titleLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        bindViewModel()
    }

    func bindViewModel() {

    }

    // Then, you can inject ViewModel in prepare function
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        super.prepare(for: segue, sender: sender)

        guard let myViewController = segue.destination as? MyViewController else { return }
        myViewController.viewModel = MyViewModel(myDependency: myDependency)
    }
}

// ------------------------------------------------------------------------------------------
// Option 2, when you use xibs
// ------------------------------------------------------------------------------------------

import Foundation

class MyViewController {

    let viewModel: MyViewModel

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var titleLabel: UILabel!

    init(viewModel: MyViewModel) {
        self.viewModel = viewModel

        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        bindViewModel()
    }

    func bindViewModel() {

    }

    // Then, you can inject ViewModel in constructor:
    func showMyViewController() {
        let vm = MyViewModel(myDependency: myDependency)
        let vc = MyViewController(viewModel: vm)
        self.present(vc, animated: true, completion: nil)
    }
}
于 2020-02-13T15:49:47.580 回答