Rxswift: withLatestFrom for multiple observables

Created on 14 May 2019  Â·  24Comments  Â·  Source: ReactiveX/RxSwift

withLatestFrom is defined only for one other observable. However, It’s a quite common case, then I have to fetch last events from few other sequences after source sequence emits. I have to handle it with idiom like this:

let info = Observable.combineLatest(source1, source2, source3)
button.rx.tap.withLaterstFrom(info)

Would not it be better to write this in one line, passing all source to .withLatestFrom? .withLatestFrom can be de defined for different arity like .combineLatest does.

button.rx.tap.withLatestFrom(source1, source2, source3)

Is this something you'd want to add? If not, I'd open a PR for RxSwiftExt.

Most helpful comment

@scotteg the OP lays out the obvious solution, source.withLatestFrom(Observable.combineLatest(source1, source2, source3)) and I don't think there's a need for an operator that would be hardest to describe than this simple combination.

What we need is more a cookbook for these situations :)

All 24 comments

This seems to me much more fitting for Ext.

Creating an operator that masks the usage of withLatestFrom + combineLatest seems wrong to me, and it also bares a different meaning.

Open to other contributors' opinions ofc.

I believe it can be implemented without using of .withLatestFrom and .combineLatest. This addition will just prevent useless boilerplate code to be written. The second code sample describes my intention "get latest events when a button is tapped" in a much more clear way. I don't have to create useless variable info in local scope.
It's ok for me to have it in RxSwiftExt, will wait until other opinions :)

This is something I’ve been wanting, too. Doesn’t matter to me whether it’s in core or ext.

The use case is that I want to subscribe to a single observable and mix in the latest from one or more other observables. I don’t want to react to emissions from those other observables. I only want to get their latest value when the subscribed-to observable emits.

On May 14, 2019, at 12:49 PM, Anton Nazarov notifications@github.com wrote:

I believe it can be implemented without using of .withLatestFrom and .combineLatest. This addition will just prevent useless boilerplate code to be written. The second code sample describes my intention "get latest events when a button is tapped" in a much more clear way. I don't have to create useless variable info in local scope.
It's ok for me to have it in RxSwiftExt, will wait until other opinions :)

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUC5PB7DNPPOJVG5YKTDPVL3RTA5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMIHLQ#issuecomment-492340142, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUC4VSX6RNUR35GLLWTTPVL3RTANCNFSM4HM3BQXQ.

would be a great feature to have; obviously under the hood it might make use of a few operators but it _feels_ like an obvious overload that is surprising to be missing at times. as far as where it ends up, I'm pretty agnostic as well... A case could be made either way.

@scotteg the OP lays out the obvious solution, source.withLatestFrom(Observable.combineLatest(source1, source2, source3)) and I don't think there's a need for an operator that would be hardest to describe than this simple combination.

What we need is more a cookbook for these situations :)

Couldn't agree with @fpillet more. 💯

@fpillet Could you please explain how in…

source.withLatestFrom(Observable.combineLatest(source1, source2, source3))

… source1 would be the only observable whose emissions triggered a subscription handler?

On May 14, 2019, at 3:02 PM, Shai Mishali notifications@github.com wrote:

Couldn't agree with @fpillet https://github.com/fpillet more. 💯

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUC6QO2ZWBLBQEUX7JJDPVMLGFA5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMT62Y#issuecomment-492388203, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUCZK4QIN3BIFRZH3BL3PVMLGFANCNFSM4HM3BQXQ.

@scotteg only source would be considered as a trigger. source1, source2 and source3 are attached data emitted with their latest value whenever source fires.

an emission from source will "pick" the latest combination of source1, source2 and source3. The combineLatest part of the withLatestFrom doesn't cause a trigger, since the "main" trigger is source in this case.

Right, that’s the whole point. What if I don’t want sources 2, 3, etc. to trigger handling, but I only want to mix in their latest values?

On May 14, 2019, at 3:07 PM, Florent Pillet notifications@github.com wrote:

@scotteg https://github.com/scotteg only source would be considered as a trigger. source1, source2 and source3 are attached data emitted with their latest value whenever source fires.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUC3FB4ZQX5SBIAH6B2DPVMLYPA5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMUMAI#issuecomment-492389889, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUC3IDRW6DTXZDQDFOADPVMLYPANCNFSM4HM3BQXQ.

@scotteg as mentioned, sources from the combineLatest operator will NOT trigger emission. Only source will.

The withLatestFrom operator is pretty clear on this: "emit when the source sends a new value, and attach the latest value from the observable passed as a parameter."

Retrospectively I see how source.withLatestFrom(source1, source2, source3) could be simpler to write but this change won't help with understanding the basic behavior of withLatestFrom.

This said, if this addition had to be made it should probably be part of core, not ext.

What if I don’t want source — the observable created by getting the latest values from combining multiple observables, each of which would trigger source to emit when it emits — to trigger handling, but I only want source1 to trigger handling, and just mix in the latest values from source2, source3, etc.

This is a very common use case, at least in my travels.

On May 14, 2019, at 3:09 PM, Florent Pillet notifications@github.com wrote:

@scotteg https://github.com/scotteg but as we mentioned, sources from the combineLatest operator will NOT trigger emission. Only source will.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUC6ASJLMSULW3XV2ADDPVMMBDA5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMUSKY#issuecomment-492390699, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUCYHHYRSQCGXUFHOBTLPVMMBDANCNFSM4HM3BQXQ.

Then you want a different operator equivalent to this construct:

source.withLatestFrom(Observable.combineLatest(source1, source2, source3))
  .map { $0.1 }

No, that would only give me source1. I still want the other source’s latest values. I just don’t want the handler triggered when those observables emit.

On May 14, 2019, at 3:15 PM, Florent Pillet notifications@github.com wrote:

Then you want a different operator equivalent to this construct:

source.withLatestFrom(Observable.combineLatest(source1, source2, source3))
.map { $0.1 }
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUC6CIVRUFC7T47LBKBDPVMMW3A5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMVBWY#issuecomment-492392667, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUC3VCMITRMZOJM3KXW3PVMMW3ANCNFSM4HM3BQXQ.

I'm a bit confused by the description - it sound like the original code piece does exactly what you want.

source.withLatestFrom(Observable.combineLatest(source1, source2, source3))

Will give you source, source1, source2, and source3 whenever source emits, but not when either source1, source2 or source3 emits

Yes…

source.withLatestFrom(source1, source2, source3)

…would handle the use case I am describing.

In contrast…

source.withLatestFrom(Observable.combineLatest(source1, source2, source3))

…would not.

On May 14, 2019, at 3:13 PM, Florent Pillet notifications@github.com wrote:

Retrospectively I see how source.withLatestFrom(source1, source2, source3) could be simpler to write but this change won't help with understanding the basic behavior of withLatestFrom.

This said, if this addition had to be made it should probably be part of core, not ext.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUCYDQK5UQRCCLUDBYX3PVMMOJA5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMU3NY#issuecomment-492391863, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUCZBDT3FFOD2CBKYJPTPVMMOJANCNFSM4HM3BQXQ.

I think I don't understand you all the way. The suggested operator will do the same (basically syntactic sugar). Maybe I'm misunderstanding the OP's intent.

combineLatest will emit whenever any one of the combined observables emits.

I’m not sure if the OP wants exactly the use case I am describing, but what I am describing is the need to be able to subscribe to a single observable, and only when that observable emits does the handler fire, but I also want to mix in the latest values from 1 or more additional observables, without those observables triggering the handler.

On May 14, 2019, at 3:20 PM, Shai Mishali notifications@github.com wrote:

I think I don't understand you all the way. The suggested operator will do the same (basically syntactic sugar). Maybe I'm misunderstanding the OP's intent.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUC5O7FI6KEAYFYV36CTPVMNITA5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMVOLY#issuecomment-492394287, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUC7I6JSBRPJNLFF2BIDPVMNITANCNFSM4HM3BQXQ.

@scotteg I was wrong with the map thing but, again, withLatestFrom will fire only when its _source_ fires, not when the sequence passed in parameter fires. Therefore, a.withLatestFrom(b) will just keep the latest value of b whenever bfires, then emit this latest value when a fires.

This is exactly when you need. Observable.combineLatest lets you collect the latest value of several sequences for the same purpose, but that's about it.

The OP’s original request was to be able to use withLatestFrom with more than one observable without having to use combineLatest.

@fpillet @freak4pc sorry, you are correct and I was mistaken. The combineLatest observables will not trigger, only the source.

So yes, using withLatestFrom + combineLatest will work. However, it should also be pointed out that, in order to have access to the source’s emitted value, it also has to be passed to combineLatest. For example…

let subject = PublishSubject()
let source2 = PublishSubject()
let source3 = PublishSubject()

let disposeBag = DisposeBag()

subject.withLatestFrom(Observable.combineLatest(subject, source2, source3))
.subscribe(onNext: { int, string, double in
print(int, string, double)
})
.disposed(by: disposeBag)

source2.onNext("A")

subject.onNext(0)

source3.onNext(1.0)

subject.onNext(1)
// Prints 1 A 1.0

A better API IMO, that also addresses the OP’s original request, would be…

subject.withLatestFrom(source2, source3)

…that yields the same as the currently required implementation, i.e., the subject's, source2’s, and source3’s elements: (Int , String , Double ).

On May 14, 2019, at 4:01 PM, Florent Pillet notifications@github.com wrote:

@scotteg https://github.com/scotteg I was wrong with the map thing but, again, withLatestFrom will fire only when its source fires, not when the sequence passed in parameter fires. Therefore, a.withLatestFrom(b) will just keep the latest value of b whenever bfires, then emit this latest value when a fires.

This is exactly when you need. Observable.combineLatest lets you collect the latest value of several sequences for the same purpose, but that's about it.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxSwift/issues/1973?email_source=notifications&email_token=AAKGUCYPLWVMBVSBSW42LODPVMSDDA5CNFSM4HM3BQX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODVMY3AI#issuecomment-492408193, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKGUC677MH2SH5C6YOJ5ZLPVMSDDANCNFSM4HM3BQXQ.

One thing to remember is also that the more sources you add, the more uncertain it will be that you'll get a value at all, unless you use startWith to provide an initial value.

I see where all of this is going, and TBH the only solution to this very problem is a variant on combineLatest that you may want to call withSourceAndLatestOrNilthat takes any number of sequences as parameters and will provide the latest value from source plus an optional for each of the parameter sequences. Therefore you'd have something like:

let a: Observable<A>, b: Observable<B>, c: Observable<C>, d: Observable<D>
let obs: Observable<A,B?,C?,D?> = a.withSourceAndLatestOrNil(b,c,d)

It's pretty easy to implement and, indeed, isn't something that core can currently provide. Feel free to open a PR on RxSwiftExt for this.

Note that due to the separate A,B,C,D types etc. you'll need to provide multiple variants (preferably with the same arity as combineLatest implementations, IIRC up to 9 parameters) for this to work.

The operators that are contained in this library should be orthogonal in functionality.

If the operator can be easily get by composition, like this, that would be argument against including it here.

let info = Observable.combineLatest(source1, source2, source3)
button.rx.tap.withLaterstFrom(info)

If somebody really needs this functionality, they can easily create local extensions, that's by design.

@kzaher got your point. Will open PR to RxSwiftExt. Thank you for your attention :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lyricsboy picture lyricsboy  Â·  3Comments

apoloa picture apoloa  Â·  3Comments

gaudecker picture gaudecker  Â·  3Comments

marlowcharite picture marlowcharite  Â·  3Comments

tyregor picture tyregor  Â·  3Comments