swift - 使用泛型将额外的道具注入视图模型
问题描述
在这篇博文之后,我在我的应用程序中创建了一个 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
}
}
}
视图控制器
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
}
}
视图模型
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
在配置文件场景的情况下。
这种模式使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
}
}
推荐阅读
- php - PHP中JSON多维数组的访问值
- google-sheets - 在一个数组中,返回除一个以外的所有值
- ruby - 通过 Scoop 在 AppVeyor CI 上安装某些工具时出现构建错误
- gradle - Gradle:在某个任务中顺序执行任务
- python-3.6 - Python3.6 如何逐行阅读并用逗号分隔这些行?
- android - 在单独的 Activity 中将 EditText 输入添加到 ListView
- c# - 从 Web Api 返回 XML
- python-3.x - os.listdir 命令未按正确的存储顺序选择文件
- javascript - 在jquery中访问父第二个孩子
- regex - 如何在 Crystal lang 中将 .match 的结果作为字符串值访问