我正在使用Swinject
作为我的DI解决方案,并使用SwinjectStoryboard
扩展名进行扩展。
我正在努力将正确的viewModel
动态注入特定的viewContoller
。具体方案如下:
MyViewController
有一个名为var viewModel: ViewModeling
的属性。
有两种不同的视图模型符合ViewModeling
协议,我们可以调用它们:firstViewModel
和secondViewModel
。
我的故事板只包含一个控制器及其MyViewController
。
问题
动态地将右视图模型注入MyViewController
的依赖项(因此只有在运行时我才会知道是注入第一个还是第二个)
我能够在服务级别上执行此操作(2个服务符合的协议,以及2个不同的viewModel,每个使用不同的服务可以使用特定名称解析所需的服务)
我正在努力在viewController级别上执行此操作,尝试向同一视图控制器注入特定的viewModel(两者都符合相同协议的提及)。
目前我的预感是SwinjectStoryboard
不允许我使用其故事板ID实例化视图控制器(就像我通常那样),此外还定义了几个不同的名称,这些名称将在运行时解析。
我错过了什么吗?
答案 0 :(得分:1)
你没有错过任何东西 - 你正在寻找的行为目前无法通过SwinjectStoryboard实现。
您可以有多个storyboardInitCompleted
使用不同的names
,但它们对应于在storyboard参数swinjectRegistrationName
中输入的名称(有关详情,请参阅docs) - 按顺序要使用它,您需要在故事板中拥有视图控制器的多个副本。
从我的观点来看,理想的解决方案是注册参数,即您将enum ViewModelType {}
使用storyboardInitCompleted
来解析正确的视图模型。很遗憾,此功能尚未最终确定(请参阅this 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")
答案 1 :(得分:1)
我的模式,如何向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)
}
}