How to properly combine multiple Drivers with RxSwift?
Asked Answered
T

1

7

I'm combining a viewDidAppear and filter Drivers with RxSwift. And they work great. But when I introduce a third Driver, it stops calling flatMapLatest on the latest combine.

In my View Controller, I have these Drivers:

let filter: Driver<String>
let viewDidAppear: Driver<Void>
let refresh: Driver<Void>

And in my view model:

// On viewDidAppear, I download a list of portfolios

let viewDidAppearPortfolios = viewDidAppear
    .flatMapLatest({ (_) -> Driver<Result<[PortfolioModel]>> in        
        return networkService.request(Router.portfolios)!
            .responseCollections()
            .trackActivity(fetching)
            .asDriver(onErrorJustReturn: .failure(NSError()))

    })
    .flatMapLatest({ (result: Result<[PortfolioModel]>) -> Driver<[PortfolioModel]> in

        switch result {
        case .success(let value): return Driver.just(value)
        case .failure(_): return Driver.just([])
        }

    })

// Then I combine with a filter from my search bar.

self.portfolios = Driver.combineLatest(viewDidAppearPortfolios, filter)
    .flatMapLatest { (portfolios: [PortfolioModel], filter: String) -> Driver<[PortfolioModel]> in

        if filter.isEmpty {
            return Driver.just(portfolios)
        }

        return Driver.just(portfolios.filter({ (portfolio) -> Bool in
            portfolio.portfolio.localizedCaseInsensitiveContains(filter)
        }))

    }

The above works!

The network requests a list of portfolios, and I'm able to filter those results as I type, client side.

However, I'd like for the user to pull to refresh, and trigger the network request again! And so, I combine with my refresh driver.

And this:

Driver.combineLatest(viewDidAppearPortfolios, filter)

Becomes this:

Driver.combineLatest(viewDidAppearPortfolios, filter, refresh)

Problem!

After combining with refresh the flatMapLatest is no longer called on viewDidAppear! Only if I manually pullToRefresh.

Driver.combineLatest(viewDidAppearPortfolios, filter, refresh).flatMapLatest { _,_,_ in 
    // No longer get's called on viewDidAppear after combining with refresh
}
  1. The viewDidAppearPortfolios still executes, so the network request is getting called!
  2. Only if I manually pull to refresh do I get the list of portfolios that I previously requested...

Any idea why?

Thank you!

Thom answered 11/7, 2017 at 16:39 Comment(0)
S
16

It looks like your refresh didn't emit a single event yet and so the combineLatest is not computed. I tried this code to test:

let one = Driver.just(1)
let two = Driver.just(2)
let three: Driver<Int> = .just(3)

let result = Driver.combineLatest(one, two, three)
    .flatMapLatest {
        return Driver.just($0 + $1 + $2)
    }

result.drive(onNext: {
    print($0)
})

This prints 6 but if you use let three: Driver<Int> = .empty() this is not printing anything. So I guess you need a way to set an initial value to refresh stream.

Stadiometer answered 11/7, 2017 at 17:20 Comment(2)
That was it, thank you. In my case I added the startWith to my refreshControl: refreshControl.rx.controlEvent(.valueChanged).startWith(()).asDriver(onErrorJustReturn: ())Thom
Driver.combineLatest is omg to me. I've been using Observables.merge for those drivers with covered in .asObservable().Cormac

© 2022 - 2024 — McMap. All rights reserved.