3

I wrote small demo code as following. I made two PublishSubject of different types. as I change any, page triggers I need to get page trigger only when one changes, which of observable_page.

class ViewController: UIViewController {

    func loadData(page: Int, keyword: String) -> Observable<[Int]> {
        let _result = Observable.of([1,2,3,4])
        return _result
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let observable_keyword = PublishSubject<String>()
        let observable_page = PublishSubject<Int>()
        let trigger_tap = PublishSubject<Void>()

        let tapObservable = trigger_tap.debug("trigger_tap", trimOutput: true)
        let stringObservable = observable_keyword.debug("stringObservable", trimOutput: true)
        let pageObservable = observable_page.debug("pageObservable", trimOutput: true)

        let request_call_trigger = Observable.combineLatest(tapObservable, pageObservable)
            .debug("request_call_trigger", trimOutput: true)

        let page = request_call_trigger
            .withLatestFrom(stringObservable) { ($0, $1) }
            .flatMap { ((_, _, page), keyword) in
                Observable.combineLatest(Observable.just(page), self.loadData(page: page, keyword: keyword)) { (pageNumber: $0, movies: $1) }
                    .materialize()
                    .filter { $0.isCompleted == false }
            }
            .share()

        observable_keyword.onNext("breaking bad")
        observable_page.onNext(1)

        trigger_tap.onNext(())

        observable_keyword.onNext("orange is new black")
        observable_keyword.onNext("orange")

    }

    let bag = DisposeBag()
}

I read some option, felt filter or ignore may work here, but as I need their value in next, so confused, how to apply it properly.

Sam Shaikh
  • 1,596
  • 6
  • 29
  • 53

2 Answers2

5

If a single trigger is what you're looking for, I think the operator you seek is withLatestFrom :

observable2
  .withLatestFrom(observable1)

Means: Only when observable2 changes - get its latest emitted value together with the latest emitted value of observable1.

edit: If you want both values, you might need to provide a resultsSelector:

observable2
  .withLatestFrom(observable1) { ($0, $1) }
Shai Mishali
  • 9,224
  • 4
  • 56
  • 83
  • But I need to pass both to next coming, like an API call with two parameters, it fails there, as withLatest only gives one value to next one. not one which observable2 holds – Sam Shaikh Oct 28 '18 at 20:28
  • 1
    Did you try this code? I believe you're mistaken. It brings both values when observable2 changes. You might need to provide a resultSelector though. – Shai Mishali Oct 28 '18 at 20:30
  • Expected parameter name followed by ':' Getting this error. I improved demo based on your answer and my Q, let me edit Q – Sam Shaikh Oct 28 '18 at 20:56
  • One caveat of using the `withLatestFrom` operator that is worth mentioning: it drops all emissions from the source observable until the second observable emits its first event – Felipe Kenji Shiba Mar 10 '21 at 20:52
  • Yup but it makes sense. The way of thinking is reverse, it doesn't drop anything. It takes the latest of the second - every time the first one emits. If the first one emits and there's nothing in the second, there is nothing for this operator to do (no "latest"). – Shai Mishali Mar 11 '21 at 09:19
0

You can use withLatestFrom which as per documentation

Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any

in your code request_call_trigger is of type Observable<(Void, Int>) which you combine with the latest value from stringObservable using withLatestFrom. This produces another tuple of type Observable<(((Void, Int), String)>

you can flatMap on the tuple created by using withLatestFrom and pass the page and keyword values to loadData method by using tuple value for page which is $0.1 so no need to use combineLatest inside flatMap.

        let tapObservable = trigger_tap.debug("trigger_tap", trimOutput: true)
        let stringObservable = observable_keyword.debug("stringObservable", trimOutput: true)
        let pageObservable = observable_page.debug("pageObservable", trimOutput: true)

        let request_call_trigger: Observable<(Void, Int)> = Observable.combineLatest(tapObservable, pageObservable)
            .debug("request_call_trigger", trimOutput: true)

        _ = request_call_trigger
            .withLatestFrom(stringObservable) { ($0, $1) }
            .flatMap { [weak self] (request_page_tuple: (Void, Int), keyword: String) -> Observable<Event<[Int]>> in
                guard let strongSelf = self else {
                    return Observable.empty()
                }
                return strongSelf
                    .loadData(page: request_page_tuple.1, keyword: keyword)
                    .materialize()
                    .filter { !$0.isCompleted }
            }
            .share()
Suhit Patil
  • 11,748
  • 3
  • 50
  • 60