简体   繁体   English

带有RxSwift的MVVM

[英]MVVM with RxSwift

I'm trying to understand mvvm + RxSwift but I got some questions. 我试图了解mvvm + RxSwift,但我遇到了一些问题。

I'm currently using this approach which I'm not sure if is the right or can be better. 我目前正在使用这种方法,但不确定是正确的方法还是更好的方法。 How can I do to like grouping the methods, I mean, maybe something like doFirst(loading = true).doNext(getData).doLast(loading = false).catch(apiError) then subscribe to this event? 我如何才能喜欢对方法进行分组,我的意思是,也许像doFirst(loading = true).doNext(getData).doLast(loading = false).catch(apiError)之类的然后订阅此事件? It's possible? 这是可能的?

ViewController: ViewController:

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)
    }
}

Then in my UsersViewModel: 然后在我的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)
            })
    }
}

My 'GET' function just returns a Observable<Several<User>>. 我的“ GET”函数只返回一个Observable<Several<User>>.

Several: 一些:

struct Several {
    var items: [User]
}

Is there any improvements that I can do? 有什么我可以做的改进吗?

It's a little hard to understand what you're asking, but if you're concerned about the imperative nature of your init method, and want to wrap your API call into a continuous Observable sequence that can be repeated, you could do something like this: 很难理解您的要求,但是如果您担心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([])
            }
    }
}

Then, you need only bind a control to this AnyObserver , or send it an event manually: 然后,您只需AnyObserver控件绑定到此AnyObserver ,或手动将事件发送给它:

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

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

Footnote 1: I typically like to make my view models struct s so that I don't have to worry about all the [weak self] statements. 脚注1:我通常希望将视图模型struct s,这样我就不必担心所有[weak self]语句。

Footnote 2: Notice how the fetchUsers() function catches any errors thrown and does not let the error propagate to the outer Observable sequence. 脚注2:请注意fetchUsers()函数如何捕获所有引发的错误,并且不允许该错误传播到外部的Observable序列。 This is important because if this outer Observable emits an error event, it can never emit another next event. 这很重要,因为如果此外部Observable发出error事件,则它将永远不会再发出next事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM