Hi,
I'm talking about the operator described in: http://reactivex.io/documentation/operators/defaultifempty.html
This can currently be achieved using toArray but the code becomes unreadable and long.
With permission, I would love to give it a shot myself
Hi @maxandron ,
can you maybe provide me with some context why do you need this operator? I'm trying to figure out how common is your use case, and where should we implement this operator.
This is really useful when I would like to switch behavior of an API or wrap it.
Specifically in my code it goes like this (for switchIfEmpty):
I have a server api that returns an Observable that emits a User object
func getUser() -> Observable<User>
If the function did not find a user, then the observable is empty.
I have a login api that wraps the server api with the following logic:
If no user was found -> create a new one.
and it looks like this:
getUser().switchIfEmpty { createNewUser() }
I actually have several more use-cases in my code with both defaultIfEmpty and switchIfEmpty. Currently I implemented it as an extension that uses toArray and flatMap. This is really un-intuitive though, especially for new users of rx.
Hi @maxandron ,
it is obviously possible to use Rx the way you are using it now, but I would suggest using Observable<User?> in case user is optional. In that way users of your API have information that this API can return User or nothing and you can use Swift compiler to make sure that who ever is using the API has to handle that case.
Then you can just implement swiftchIfNil like this or just use RxOptional.
I'm not saying that there is anything wrong with the way you are doing it now or that it doesn't work, but I wouldn't want to add new operators unless we have really compelling reasons to maintain them :)
Hi,
I personally avoid using optionals in public apis (I try to limit the use of nil to small contained scopes).
But I see your point. Let me try and give you another use case then:
I have another api that looks like this
func getArticlesBy(headline: Headline) -> Observable<Article>
and I have `func getArticlesBy(keywords: [Keyword]) -> Observable
This function might emit 1 article, 5 or zero.
I wrap this api like this:
func getArticles() -> Observable<Article> {
return getArticlesBy(Headline(headline)) //
.switchIfEmpty { getArticlesBy(splitToKeywords(headline)) }
}
Here, returning nil is counter intuitive.
Hey @maxandron ,
Not saying that there is anything wrong with your approach, but I can maybe provide my personal interpretation.
We are talking about different ways to model APIs that return array of items.
func getArticles() -> Observable<Article>
func getArticles() -> Observable<[Article]>
Since observable sequences are sequences, it is possible to interpret the semantics of observable sequences differently.
The way I prefer to interpret observable sequences is a sequence of elements over time. That _over time_ part is important.
For example, if you model your API this way:
func getArticles() -> Observable<[Article]>
then you will be able to replace implementation in future from one database query or HTTP request, to observing database because it's easy to differentiate snapshots of results over time.
If you model your API that returns multiple result elements calculated immediatelly as observable sequence elements (which can be done)
func getArticles() -> Observable<Article>
... then you are losing part of freedom to replace implementation later to be something more sophisticated.
When reading this
Observable<[Article]>
... I as an API consumer get an information, this API will return snapshots of state and each value is represented as array of Articles.
When reading this
Observable<Article>
... I as an API consumer get an information, this API will return snapshots of state and each value is just an Article. The part that those batches of articles are produced immediately is lost in the API signature.
I got a lot of food for thought from this @kzaher, thank you!
Alright, it took me some time to figure out what I really think about this.
And you make great points, your way of thinking somewhat differs (imo) from the direction RxJava is going in (With Single, Completable and such).
You see no use for those (Correct me if I'm wrong) since you can just structure your api to be more concise (with optionals and arrays).
And it is a good way I suppose, because It does not require the user to check out the documentation in order to understand what the api produces.
But there are two problems I see here:
Going back to the articles example
If I have an API that returns Observable<[Article]> I essentially say that onNext will be called only when all Article objects are ready.
On the other hand, when I structure the api with Observable<Article> I know I can get a variable number of articles, and I don't need to have any information on when I receive them.
Receive article -> display it -> wait for next one
I feel like in your way of thinking, Observables design is somewhat lost. Because if we know onNext will be called only once, there is no need for onComplete. This is essentially treating observables as 'Single's (which does not exist currently in RxSwift)
Hi @maxandron ,
I think there might be a slight misunderstanding with interpretation of my last response :)
I think we could theoretically pull this into this repo if we are talking about switchIfEmpty.
I'm curious how many other users need this operator. If you need it, please upvote this message, or downvote if you don't think we should include it.
If we were to pull this, let's cosider this for RxSwift 3.0. We need to do a bunch of refactorings for Swift 3.0 as it is because of new naming conventions :(, so trying to reduce the unnecessary work.
Hey @kzaher and @maxandron , sorry for opening a duplicate issue (#1000) instead of replying to this one. I've ran into similar situation when my company was developing an Android/iOS mobile app pair. The need for the operator is increased especially when you are developing for multiple platforms with different ReactiveX implementations (e.g. RxJava has both defaultIfEmpty and switchIfEmpty operators and RxSwift doesn't) .
Like I wrote in the #1000 I have an implementation for those operators ready in the form of Swift extension for ObservableType classes.
Hi guys,
ok, I think I can recognize valid points here. I would be fine with including these ones in the main repo.
They seem orthogonal and it looks like there are practical use cases that I have overlooked.
@NitroNbg I'm curious how you've implemented those operators. Feel free to make a PR or send me a link to some commit where you've implemented them. There is a slight difference when implementing operators internally or externally, so we can see what would be the most efficient way to get them into this repo.
Hey @kzaher , you can find my implementation here. It's not documented or even well-formatted. But it's a starting point.
EDIT: Also, I'm having some trouble with the test bash scripts. Not that it fails some tests, it won't run at all :(
Hi @NitroNbg ,
there are some problems with your current implementation
let value = asObservable()
var validResult = false
should be inside Observable.create scope.
If somebody wants to create a PR, that would be great.
Hey guys, I'll have some time today.
Can I give it a try?
@sergdort Sure you can ;) I'll have time to work on this during weekend. But by all means go ahead and make your own PR, even better if you can do it today.
Hi, sorry for being dormant. I wasn't feeling very well. I hope I'll have time to squeeze in a PR tonight (in about five hours)
We've merge these operators to master. We'll release it soon.
Most helpful comment
Hi @maxandron ,
I think there might be a slight misunderstanding with interpretation of my last response :)
I think we could theoretically pull this into this repo if we are talking about
switchIfEmpty.I'm curious how many other users need this operator. If you need it, please upvote this message, or downvote if you don't think we should include it.
If we were to pull this, let's cosider this for RxSwift 3.0. We need to do a bunch of refactorings for Swift 3.0 as it is because of new naming conventions :(, so trying to reduce the unnecessary work.