ios - 带有 RxSwift 的 MVVM
问题描述
我试图理解 mvvm + RxSwift 但我有一些问题。
我目前正在使用这种方法,我不确定它是否正确或可以更好。我该怎么做才能喜欢对方法进行分组,我的意思是,可能类似于 doFirst(loading = true).doNext(getData).doLast(loading = false).catch(apiError) 然后订阅此事件?这是可能的?
视图控制器:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
viewModel = UsersViewModel(apiService: apiService)
configureBindings()
}
func configureBindings() {
tableView.delegate = nil
tableView.dataSource = nil
viewModel.isLoading.bind(to: loadingView.rx.isAnimating)
.disposed(by: disposeBag)
viewModel.models
.bind(to: tableView.rx.items(cellIdentifier: "userCell", cellType: UserCell.self)) {(_, _, cell) in
print("Binding the cell items")
}.disposed(by: disposeBag)
tableView.rx.modelSelected(User.self).subscribe(onNext: { value in
print(value)
}).disposed(by: disposeBag)
viewModel.error.filterNil().subscribe(onNext: { (err) in
self.tableView.backgroundView = EmptyView(title: "No Users", description: "No users found")
print("Showing empty view...")
print(err)
}).disposed(by: disposeBag)
}
}
然后在我的用户视图模型中:
class UsersViewModel {
var models: Observable<[User]> {
return modelsVariable.asObservable()
}
var isLoading: Observable<Bool> {
return isLoadingVariable.asObservable()
}
var error: Observable<ApiError?> {
return errorVariable.asObservable()
}
private var modelsVariable = BehaviorRelay<[User]>(value: [])
private var isLoadingVariable = BehaviorRelay<Bool>(value: false)
private var errorVariable = BehaviorRelay<ApiError?>(value: nil)
// MARK: - Data Manager
var apiService: API
required init(apiService: API) {
self.apiService = apiService
isLoadingVariable.accept(true)
apiService.GET(EndPoints.USER_LIST, type: Several<User>.self)
.subscribe(onNext: { (model) in
self.isLoadingVariable.accept(false)
self.modelsVariable.accept(model.items)
}, onError: { (err) in
self.isLoadingVariable.accept(false)
self.errorVariable.accept(err as? ApiError)
})
}
}
我的 'GET' 函数只返回一个Observable<Several<User>>.
一些:
struct Several {
var items: [User]
}
有什么我可以做的改进吗?
解决方案
有点难以理解你在问什么,但如果你担心你的init
方法的命令性质,并且想将你的 API 调用包装成一个可以重复的连续 Observable 序列,你可以这样做:
class UsersViewModel {
//...
var fetchUsersObserver: AnyObserver<Void> {
return fetchUsersSubject.asObserver()
}
//...
private let fetchUsersSubject = PublishSubject<Void>()
private let disposeBag = DisposeBag()
//...
required init(apiService: API) {
self.apiService = apiService
bindFetchUsers()
}
private func bindFetchUsers() {
fetchUsersSubject
.asObservable()
.do(onNext: { [weak self] _ in self?.isLoadingVariable.accept(true) })
.flatMap(self.fetchUsers)
.do(onNext: { [weak self] _ in self?.isLoadingVariable.accept(false) })
.bind(to: modelsVariable)
.disposed(by: disposeBag)
}
private func fetchUsers() -> Observable<[User]> {
return apiService
.GET(EndPoints.USER_LIST, type: Several<User>.self)
.map { $0.items }
.catchError { [weak self] error in
self?.errorVariable.accept(error as? ApiError)
return .just([])
}
}
}
然后,您只需要将控件绑定到 this AnyObserver
,或手动向其发送事件:
func configureBindings() {
// from a control, such as UIButton
someButton
.rx
.tap
.bind(to: viewModel.fetchUsersObserver)
.disposed(by: disposeBag)
// manually
viewModel.fetchUsersObserver.onNext(())
}
脚注 1:我通常喜欢将我的视图模型设为struct
s,这样我就不必担心所有的[weak self]
语句。
脚注 2:注意fetchUsers()
函数如何捕获任何抛出的错误,并且不让错误传播到外部 Observable 序列。这很重要,因为如果这个外部 Observable 发出一个error
事件,它就永远不会发出另一个next
事件。
推荐阅读
- windows - HttpSendRequest 失败并出现错误 12152,但 InternetOpenUrl 没有
- python - 保存和加载由 gensim 相似性模型制作的多个分片
- ruby - Ruby gem 的单个可执行二进制文件
- java - 如何以编程方式在后台运行应用程序?
- spring - 试图模拟 restClient 外部 API,但它正在调用 java 中的实际 API
- server-side - 如何使用价格验证请求正文
- c - 如何使用 C 将 Windows PC 的 MAC 地址分配给字符串变量?
- markdown - 如何更改使用 Markdown 创建的幻灯片中的字体大小?
- angular - 将大量自定义图标注册到 mat-icon
- c - 指针类型的使用