Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 289
nmdias Avatar asked Jul 11 '17 16:07

nmdias


People also ask

Is combine better than RxSwift?

In contrast to RxSwift, Combine also allows subscribers to request more values at any time, resulting in backpressure support. So, rather than the publisher deciding when values are propagated, it is actually the subscriber demanding new values.

What is a driver RxSwift?

RxSwift: Driver Go from: a single observable that updates the entire UI to bindTo and reuse the same observable across the viewController.


1 Answers

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.

like image 72
Fabio Felici Avatar answered Nov 11 '22 08:11

Fabio Felici