如何将正确的 viewModel 注入单个 viewController
Posted
技术标签:
【中文标题】如何将正确的 viewModel 注入单个 viewController【英文标题】:How to inject the right viewModel to a single viewController 【发布时间】:2018-04-09 11:52:11 【问题描述】:我将 Swinject
用于我的 DI 解决方案,并使用 SwinjectStoryboard
扩展名对其进行扩展。
我正在努力将正确的viewModel
动态注入特定的viewContoller
。具体场景如下:
MyViewController
有一个名为var viewModel: ViewModeling
的属性。
有两种不同的视图模型符合ViewModeling
协议,我们称之为:firstViewModel
和secondViewModel
。
我的故事板只包含一个控制器及其MyViewController
。
问题
动态注入正确的 viewModel 作为 MyViewController
的依赖项(所以只有在运行时我才会知道是注入第一个还是第二个)
我能够在服务级别上做到这一点(2 个服务遵循的一个协议,以及每个使用不同服务的 2 个不同 viewModel 可以使用特定名称解析所需的一个)
我正在努力在 viewController 级别上执行此操作,尝试将相同的视图控制器注入特定的 viewModel(两者都符合相同的协议)。
目前我的预感是 SwinjectStoryboard
不允许我使用它的故事板 ID 实例化视图控制器(就像我通常会做的那样),此外还定义了几个将在运行时解析的不同名称。
我错过了什么吗?
【问题讨论】:
【参考方案1】:您没有错过任何东西 - 您正在寻找的行为目前确实无法通过 SwinjectStoryboard 实现。
您可以拥有多个 storyboardInitCompleted
和不同的 names
,但它们对应于在情节提要参数 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")
【讨论】:
我觉得应该是return vc【参考方案2】:我的模式,如何将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)
【讨论】:
以上是关于如何将正确的 viewModel 注入单个 viewController的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Xamarin 和 Autofac 将构造函数依赖项注入 ViewModel?
如何使用 Knockout.js 将多个 View 绑定到单个 ViewModel
使用 Swinject 将 ViewModel 条件注入 ViewController
如何使用 Xamarin.Forms.DependencyService 注入具有构造函数注入的 ViewModel
无法使用 Dagger2 将对象注入 ViewModel 类
如何在 Android/Kotlin App 上通过 Koin 注入在 BaseActivity 中初始化/注入通用 ViewModel