带有 RxSwift 的 MVVM

Posted

技术标签:

【中文标题】带有 RxSwift 的 MVVM【英文标题】:MVVM with RxSwift 【发布时间】:2018-04-29 08:26:49 【问题描述】:

我正在尝试理解 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)
    

然后在我的 UsersViewModel 中:

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&lt;Several&lt;User&gt;&gt;.

几个:

struct Several 
    var items: [User]

有什么可以改进的吗?

【问题讨论】:

【参考方案1】:

有点难以理解您在问什么,但是如果您担心 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([])
            
    

那么,你只需要将一个控件绑定到这个AnyObserver,或者手动给它发送一个事件:

func configureBindings() 
    // from a control, such as UIButton
    someButton
        .rx
        .tap
        .bind(to: viewModel.fetchUsersObserver)
        .disposed(by: disposeBag)

    // manually
    viewModel.fetchUsersObserver.onNext(())

脚注 1:我通常喜欢将我的视图模型设为 structs,这样我就不必担心所有 [weak self] 语句。

脚注 2:注意fetchUsers() 函数如何捕获任何抛出的错误,并且不让错误传播到外部 Observable 序列。这一点很重要,因为如果这个外部 Observable 发出一个 error 事件,它就永远不会发出另一个 next 事件。

【讨论】:

以上是关于带有 RxSwift 的 MVVM的主要内容,如果未能解决你的问题,请参考以下文章

带有 UITextFieldDelegate 的 RxSwift 控制事件

带有 RxSwift 的 MVVM

带有 rxswift 的多张地图

RxSwift:返回一个带有错误的新可观察对象

带有 RxSwift 的搜索栏取消按钮

如何在带有 RxSwift 的驱动程序上使用 flatMapLatest