使用泛型将额外的道具注入视图模型
Injecting additional props into a viewmodel using generics
在 this blog post 之后,我在我的应用程序中创建了一个 MVVM 模式。
我有以下 -
protocol ViewModelType {
associatedtype Dependency
associatedtype Bindings
init(dependency: Dependency, bindings: Bindings)
}
enum Attachable<VM: ViewModelType> {
case detached(VM.Dependency)
case attached(VM.Dependency, VM)
mutating func bind(_ bindings: VM.Bindings) -> VM {
switch self {
case let .detached(dependency):
let vm = VM(dependency: dependency, bindings: bindings)
self = .attached(dependency, vm)
return vm
case let .attached(dependency, _):
let vm = VM(dependency: dependency, bindings: bindings)
self = .attached(dependency, vm)
return vm
}
}
}
ViewController
final class StartViewController: BaseViewController<StartView> {
var viewModel: Attachable<StartViewModel>!
var bindings: StartViewModel.Bindings {
let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear))
.mapToVoid()
.asDriverOnErrorJustComplete()
return StartViewModel.Bindings(
checkAuthState: viewWillAppear
)
}
}
extension StartViewController: ViewModelAttaching {
func bind(viewModel: StartViewModel) -> StartViewModel {
return viewModel
}
}
ViewModel
final class StartViewModel: ViewModelType {
typealias Dependency = HasAuthService
let authCheck: Driver<Void>
struct Bindings {
let checkAuthState: Driver<Void>
}
private let disposeBag = DisposeBag()
init(dependency: Dependency, bindings: Bindings) {
authCheck = bindings.checkAuthState
.flatMap {
return dependency.authSvc.checkSession()
.mapToVoid()
.asDriver(onErrorJustReturn: ())
}
}
}
这使我可以按如下方式设置我的视图 -
let viewController = StartViewController()
let avm: Attachable<StartViewModel> = .detached(dependencies)
let viewModel = viewController.attach(wrapper: avm)
navigationController.setViewControllers([viewController], animated: false)
这很好用,很容易测试,我对它的工作方式很满意。然而,我有一个场景,我需要将一些额外的道具传递到我的 ViewModel
中。
比如一个userId
在profile场景的情况下
这个模式让 init
方法远离所以我需要一种方法来通过 .detached(dependencies)
注入额外的道具
类似于 .detached(dependencies, userId)
但是我不确定如何以通用方式执行此操作。
我试过了 - 注意添加了 Props
和 props
protocol ViewModelType {
associatedtype Dependency
associatedtype Bindings
associatedtype Props
init(dependency: Dependency, bindings: Bindings, props: Props?)
}
enum Attachable<VM: ViewModelType> {
case detached(VM.Dependency, VM.Props)
case attached(VM.Dependency, VM, VM.Props)
mutating func bind(_ bindings: VM.Bindings) -> VM {
switch self {
case let .detached(dependency, props):
let vm = VM(dependency: dependency, bindings: bindings, props: props)
self = .attached(dependency, vm, props)
return vm
case let .attached(dependency, _, props):
let vm = VM(dependency: dependency, bindings: bindings, props: props)
self = .attached(dependency, vm, props)
return vm
}
}
}
然而,这需要我用以下内容更新我的 ViewModel
-
**ViewModel**
```swift
typealias Props = <Some Prop Type>
init(dependency: Dependency, bindings: Bindings, props: Props?)
这很好用,除非我不需要注入任何道具。然后我需要为未使用的东西指定类型。
我试过了 -
typealias Props = Void?
init(dependency: Dependency, bindings: Bindings, props: Props?)
然后使用 -
设置我的视图
let avm: Attachable<StartViewModel> = .detached(dependencies, nil)
但是不得不在我所有不接受任何东西的模型中添加 typealias Props = Void?
感觉不对
我怎样才能以最可扩展、最通用的方式实现这一点?
为什么不介绍 Props
而不是隐藏 Dependency
后面的附加信息?
因此,例如,在您的情况下,您可以:
final class ProfileViewModel: ViewModelType {
let authCheck: Driver<Void>
struct Dependency {
let authSvc: HasAuthService
let userId: String
}
struct Bindings {
let checkAuthState: Driver<Void>
}
private let disposeBag = DisposeBag()
init(dependency: Dependency, bindings: Bindings) {
authCheck = bindings.checkAuthState
.flatMap {
return dependency.authSvc.checkSession()
.mapToVoid()
.asDriver(onErrorJustReturn: ())
}
// use dependency.userId
}
}
在 this blog post 之后,我在我的应用程序中创建了一个 MVVM 模式。
我有以下 -
protocol ViewModelType {
associatedtype Dependency
associatedtype Bindings
init(dependency: Dependency, bindings: Bindings)
}
enum Attachable<VM: ViewModelType> {
case detached(VM.Dependency)
case attached(VM.Dependency, VM)
mutating func bind(_ bindings: VM.Bindings) -> VM {
switch self {
case let .detached(dependency):
let vm = VM(dependency: dependency, bindings: bindings)
self = .attached(dependency, vm)
return vm
case let .attached(dependency, _):
let vm = VM(dependency: dependency, bindings: bindings)
self = .attached(dependency, vm)
return vm
}
}
}
ViewController
final class StartViewController: BaseViewController<StartView> {
var viewModel: Attachable<StartViewModel>!
var bindings: StartViewModel.Bindings {
let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear))
.mapToVoid()
.asDriverOnErrorJustComplete()
return StartViewModel.Bindings(
checkAuthState: viewWillAppear
)
}
}
extension StartViewController: ViewModelAttaching {
func bind(viewModel: StartViewModel) -> StartViewModel {
return viewModel
}
}
ViewModel
final class StartViewModel: ViewModelType {
typealias Dependency = HasAuthService
let authCheck: Driver<Void>
struct Bindings {
let checkAuthState: Driver<Void>
}
private let disposeBag = DisposeBag()
init(dependency: Dependency, bindings: Bindings) {
authCheck = bindings.checkAuthState
.flatMap {
return dependency.authSvc.checkSession()
.mapToVoid()
.asDriver(onErrorJustReturn: ())
}
}
}
这使我可以按如下方式设置我的视图 -
let viewController = StartViewController()
let avm: Attachable<StartViewModel> = .detached(dependencies)
let viewModel = viewController.attach(wrapper: avm)
navigationController.setViewControllers([viewController], animated: false)
这很好用,很容易测试,我对它的工作方式很满意。然而,我有一个场景,我需要将一些额外的道具传递到我的 ViewModel
中。
比如一个userId
在profile场景的情况下
这个模式让 init
方法远离所以我需要一种方法来通过 .detached(dependencies)
类似于 .detached(dependencies, userId)
但是我不确定如何以通用方式执行此操作。
我试过了 - 注意添加了 Props
和 props
protocol ViewModelType {
associatedtype Dependency
associatedtype Bindings
associatedtype Props
init(dependency: Dependency, bindings: Bindings, props: Props?)
}
enum Attachable<VM: ViewModelType> {
case detached(VM.Dependency, VM.Props)
case attached(VM.Dependency, VM, VM.Props)
mutating func bind(_ bindings: VM.Bindings) -> VM {
switch self {
case let .detached(dependency, props):
let vm = VM(dependency: dependency, bindings: bindings, props: props)
self = .attached(dependency, vm, props)
return vm
case let .attached(dependency, _, props):
let vm = VM(dependency: dependency, bindings: bindings, props: props)
self = .attached(dependency, vm, props)
return vm
}
}
}
然而,这需要我用以下内容更新我的 ViewModel
-
**ViewModel**
```swift
typealias Props = <Some Prop Type>
init(dependency: Dependency, bindings: Bindings, props: Props?)
这很好用,除非我不需要注入任何道具。然后我需要为未使用的东西指定类型。
我试过了 -
typealias Props = Void?
init(dependency: Dependency, bindings: Bindings, props: Props?)
然后使用 -
设置我的视图let avm: Attachable<StartViewModel> = .detached(dependencies, nil)
但是不得不在我所有不接受任何东西的模型中添加 typealias Props = Void?
感觉不对
我怎样才能以最可扩展、最通用的方式实现这一点?
为什么不介绍 Props
而不是隐藏 Dependency
后面的附加信息?
因此,例如,在您的情况下,您可以:
final class ProfileViewModel: ViewModelType {
let authCheck: Driver<Void>
struct Dependency {
let authSvc: HasAuthService
let userId: String
}
struct Bindings {
let checkAuthState: Driver<Void>
}
private let disposeBag = DisposeBag()
init(dependency: Dependency, bindings: Bindings) {
authCheck = bindings.checkAuthState
.flatMap {
return dependency.authSvc.checkSession()
.mapToVoid()
.asDriver(onErrorJustReturn: ())
}
// use dependency.userId
}
}