Search code examples
iosswiftmvvmrx-swift

Swift 5: How to get the value of a BehaviorRelay and binding with RxSwift and MVVM


I'm trying to get the amount value from the service in the View Model, and then bind it in the ViewController to an amountLabel.

This is my ViewModel:

class AmountViewModel {
    
    private let accountService: AccountManagerProtocol        
    
    let _amount = BehaviorRelay<Int>(value: 0)
    let amount: Observable<Int>
        
    private let disposeBag = DisposeBag()
   
    init(accountService: AccountManagerProtocol = AccountManager()) {

        self.accountService = accountService
    
        amount = _amount.asObservable()
        
        getAmount()   
    }

    func getAmount(){
        accountService.getAccount()
            .map{ $0.amount ?? 0 }
            .bind(to: _amount)
            .disposed(by: disposeBag)
    }
}

This is my ViewController, I did something like this, to obtain the amount of the viewModel, but I feel that it is not the best way, I would like to obtain the value of amount and be able to bind it to amountLabel in a simpler way.

private extension AmountViewController {
    private func bindViewModel() {
        amountView.titleLabel.text = viewModel.title
               
        //Get Amount
        viewModel.amount
            .observe(on: MainScheduler.instance)
            .withUnretained(self)
            .subscribe(onNext: { owner, amount in
                if let amountString = amount.currencyToString() {
                owner.inputAmountView.amountLabel.text = "BALANCE: \(amountString)"
                }
            })
            .disposed(by: disposeBag)
 }   

Solution

  • Here is the most obvious simplification:

    class AmountViewModel {
        let amount: Observable<Int>
        
        init(accountService: AccountManagerProtocol = AccountManager()) {
            amount = accountService.getAccount()
                .map { $0.amount ?? 0 }
        }
    }
    
    private extension AmountViewController {
        private func bindViewModel() {
            viewModel.amount
                .compactMap { $0.currencyToString().map { "BALANCE: \($0)"} }
                .observe(on: MainScheduler.instance)
                .bind(to: inputAmountView.amountLabel.rx.text)
                .disposed(by: disposeBag)
        }
    }
    

    But I think I would move the code in the compactMap closure into the view model...