Skip to content

withLatestFrom for multiple observables #1973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
M0rtyMerr opened this issue May 14, 2019 · 24 comments
Closed

withLatestFrom for multiple observables #1973

M0rtyMerr opened this issue May 14, 2019 · 24 comments

Comments

@M0rtyMerr
Copy link
Contributor

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.

@freak4pc
Copy link
Member

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.

@M0rtyMerr
Copy link
Contributor Author

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 :)

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@zachgrayio
Copy link

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.

@fpillet
Copy link
Contributor

fpillet commented May 14, 2019

@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 :)

@freak4pc
Copy link
Member

Couldn't agree with @fpillet more. 💯

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@fpillet
Copy link
Contributor

fpillet commented May 14, 2019

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

@freak4pc
Copy link
Member

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.

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@fpillet
Copy link
Contributor

fpillet commented May 14, 2019

@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."

@fpillet
Copy link
Contributor

fpillet commented May 14, 2019

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.

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@fpillet
Copy link
Contributor

fpillet commented May 14, 2019

Then you want a different operator equivalent to this construct:

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

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@freak4pc
Copy link
Member

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

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@freak4pc
Copy link
Member

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.

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@fpillet
Copy link
Contributor

fpillet commented May 14, 2019

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

@scotteg
Copy link
Contributor

scotteg commented May 14, 2019 via email

@fpillet
Copy link
Contributor

fpillet commented May 14, 2019

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.

@kzaher
Copy link
Member

kzaher commented May 15, 2019

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.

@M0rtyMerr
Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants