swiftrx-swiftrx-cocoa

How to properly combine multiple Drivers with RxSwift?


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!


Solution

  • 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.