https://kotlinlang.org logo
#rx
Title
# rx
i

iex

02/25/2020, 3:33 PM
I'll explain my problem in more detail. I have this in a class (more specifically Android's view model though this doesn't seem relevant)
Copy code
private val dealerAction: Single<DealerAction> = carDealersRepo.selectedCarDealer()
    .map {
        it.toDealerAction()
    }

val dealerButtonTitle: LiveData<String> = dealerAction
    .map { it.toButtonTitle() }
    .toObservable()
    .toLiveData()
When I navigate to a new screen, I change carDealersRepo's underlaying data. When I navigate back, I want that this view model re-fetches
selectedCarDealer
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 3:40 PM
The
selectedCarDealer()
method won’t be executed again, but the single it returns should be subscribed to again.
i

iex

02/25/2020, 3:40 PM
but I put a breakpoint in the code and it doesn't stop there again
Copy code
override fun selectedCarDealer(): Single<Optional<CarDealer>> =
        (preferences.getObject(SELECTED_CAR_DEALER, CarDealer::class.java)?.let {
            Optional.Some(it)
        } ?: Optional.None).let{ Single.just(it) }
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 3:41 PM
If you want to invoke the function again, do something like
dealerAction = Single.defer { carDealersRepo.selectedCarDealer() }
i

iex

02/25/2020, 3:41 PM
aha! will try it out
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 3:41 PM
Right, so the Single that is being resubscribed is your
Single.just
.
The
selectedCarDealer
method doesn’t magically get executed again
i

iex

02/25/2020, 3:43 PM
awesome it works now! Thank you!! ❤️
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 3:44 PM
I think you probably want to change your implementation of
selectedCarDealer
though, to get the behavior you’re expecting. Something like:
Copy code
override fun selectedCarDealer(): Maybe<CarDealer> =
  Maybe.fromCallable {
    preferences.getObject(…)
  }
i

iex

02/25/2020, 3:44 PM
that probably makes sense
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 3:45 PM
Wrapping some computation in
just
is usually a smell, since you usually want the computation performed on subscription.
i

iex

02/25/2020, 3:45 PM
right, the problem is the
just
. If it was
create
, it would work, I assume
👍 1
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 3:46 PM
(and
Maybe<Foo>
is semantically equivalent to
Single<Optional<Foo>>
with less typing)
i

iex

02/25/2020, 3:46 PM
yep, changing it now
and how do I get notified about whether the result is a value or
null
? in
map
I get only the success value
I need to handle the null explicitly, if there's no selection yet, the button gets a title for that
it seems that
Maybe
isn't made for that 😕
Ok, I changed
selectedCarDealer()
to this:
Copy code
override fun selectedCarDealer(): Single<Optional<CarDealer>> =
    fromCallable {
        Optional.from(preferences.getObject(SELECTED_CAR_DEALER, CarDealer::class.java))
    }
from an architecture perspective this seems bad though. I shouldn't have to depend on the implementation of
selectedCarDealer()
🤔
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 4:00 PM
and how do I get notified about whether the result is a value or
null
? in
map
I get only the success value
Ah, then
Single<Optional>>
is the way to go
👍 1
from an architecture perspective this seems bad though. I shouldn’t have to depend on the implementation of selectedCarDealer()
It’s pretty common for methods that return `Single`s that perform some computation to do that computation on subscription, because
Single
typically represents asynchronous computation. The fact that this particular method implementation doesn’t actually do anything asynchronous and always returns immediately is also an implementation detail, but it’s the more anomalous one.
i

iex

02/25/2020, 4:09 PM
I get that, but still... I should not have to think about how an interface is implemented, even if it's corner cases possibly.
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 4:09 PM
In other words, the use of
Single
as the return type here implies that this function needs to do some asynchronous work (that’s the whole point of
Single
). The way for
Single
to do asynchronous work is by kicking it off on subscription, so it’s natural to expect that any work it does will be performed on subscription.
I should not have to think about how an interface is implemented
I agree. The point I’m trying (badly) to communicate, is the fact that your interface uses
Single
at all implies that work is being done on subscription, and not just when your method is called. That’s basically the contract of
Single
. That said, if you really don’t trust your implementations to follow this convention, you can just wrap it in a
Single.defer
. But it would be better to make your implementation behave in a less surprising way to begin with, since this might trip up other callers in the future as well.
i

iex

02/25/2020, 4:14 PM
but what if I'm passing e.g. a mock repo for unit tests, where it makes sense to use
.just
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 4:14 PM
If your test doesn’t need to change the value, then that’s fine. I use
just
in tests all the time, it’s great for that. But in that case you’re hardcoding a value, not computing it from some real world resource.
i

iex

02/25/2020, 4:15 PM
I should have said integration tests... 😛
hmm
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 4:15 PM
If the value can never change, then using
just
is fine.
i

iex

02/25/2020, 4:16 PM
well, I get the reasoning of what you're saying but it still doesn't feel 100% clean from an architectural point of view. "Don't make assumptions" etc. etc.
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 4:16 PM
Another question though: since this value evidently can change over time, why are you using
Single
instead of
Observable
?
i

iex

02/25/2020, 4:16 PM
but thanks a lot in any case for the explanations
hmm... I've modeled all the api and repo calls as singles
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 4:17 PM
well, I get the reasoning of what you’re saying but it still doesn’t feel 100% clean from an architectural point of view. “Don’t make assumptions” etc. etc.
Like Jake said, this is the default behavior for
Single
. Retrofit does this too: if you have a method that returns a
Single
, the network request will be performed every time you subscribe to the single, not every time you call the method.
i

iex

02/25/2020, 4:18 PM
otherwise I'd have to implement a subject or something in the repo and notify the observers when the preference is changed... that's not what I had in mind for the overall architecture
aah, I'm understanding now
why exactly it didn't work
lol
you're right, the problem is not rx java's api
z

Zach Klippenstein (he/him) [MOD]

02/25/2020, 4:24 PM
that’s not what I had in mind for the overall architecture
This is obviously off topic from your original question, but again… why not? Rx is short for “reactive”. Reactive means that code reacts to changes and external events – things like preferences changing. The advantage of reactive UI programming is that your UI will automatically update to reflect state changes. That said, maybe you’re not trying to build a reactive UI and only use RxJava as a concurrency library – and that’s fine too.
i

iex

02/25/2020, 4:25 PM
I am, but I'm wiring everything in the view models
could reconsider though
(almost everything, there are a few dependencies, like deep links broadcaster and similar that expose observables to the view models)
in this particular case I think I did it too because it was originally intended as an api service (so the repo just forwards to the api), so I just replicated the interface
4 Views