https://kotlinlang.org logo
#coroutines
Title
# coroutines
o

Orhan Tozan

03/19/2020, 2:35 PM
Given the following:
Copy code
private val _selectedUserIds = ConflatedBroadcastChannel<List<Int>>(emptyList())
private val selectedUserIds: Flow<List<Int>> = _selectedUserIds.asFlow()
A
onSaveButtonClick()
method inside the class should send a network call with the value of selectedUserIds Should I get it's value by using the
.value
of the
ConflatedBroadcastChannel
or should I launch a coroutine and call
.collect()
on the
Flow
? What's the difference?
k

kevin.cianfarini

03/19/2020, 7:25 PM
Should be view state, right? Your VM should reactively drive view state. I would consider current user id to be view state
o

Orhan Tozan

03/19/2020, 7:25 PM
What is view state?
k

kevin.cianfarini

03/19/2020, 7:26 PM
Is this is android? or another UI application
o

Orhan Tozan

03/19/2020, 7:26 PM
This is a viewmodel on Kotlin Multiplatform level, so not bounded to any platform
k

kevin.cianfarini

03/19/2020, 7:27 PM
Okay
Give you give me a bigger picture of what's going on?
o

Orhan Tozan

03/19/2020, 7:27 PM
The given code above is all in a class named ViewModel
Those two lines are class properties
k

kevin.cianfarini

03/19/2020, 7:28 PM
how are values being propagated to the channel?
Is
onSaveButtonClick
in the view or the viewmodel?
o

Orhan Tozan

03/19/2020, 7:28 PM
Viewmodel
Same class
k

kevin.cianfarini

03/19/2020, 7:29 PM
Typically, you'd want that to be in the view. In the viewmodel, you'd want something like
handleUSerIds(ids: list<Int>)
and call that viewmodel method from the view when the save button is clicked
your viewmodel shouldn't know anything about the view
o

Orhan Tozan

03/19/2020, 7:29 PM
It doesn't
k

kevin.cianfarini

03/19/2020, 7:30 PM
It does if it's got a method declared in it called
onSaveButtonClick
o

Orhan Tozan

03/19/2020, 7:30 PM
the view already calls onSelectUser() of the viewmodel
viewmodel then updates the selectedUserIds with the conflatedbroadcastchannel.offer()
k

kevin.cianfarini

03/19/2020, 7:31 PM
okay, and you're trying to get that data back to the view, right?
In which case, you make the flow public and have the view collect the flow in the viewmodel
o

Orhan Tozan

03/19/2020, 7:32 PM
The view already can get the data, it observes the flow. The viewmodel needs to send it to the server on save button click
k

kevin.cianfarini

03/19/2020, 7:33 PM
viewmodel then updates the selectedUserIds with the conflatedbroadcastchannel.offer()
Then hook into here when you have the value. When the viewmodel is offering that value to the channel, also do a network request
o

Orhan Tozan

03/19/2020, 7:34 PM
I dont understand, can you be more specific? This would do a network request everytime the selection gets updated
k

kevin.cianfarini

03/19/2020, 7:35 PM
Is that not what you're trying to do
?
o

Orhan Tozan

03/19/2020, 7:35 PM
No, only when the save button is clicked
k

kevin.cianfarini

03/19/2020, 7:35 PM
If not, then I would maintain an updated list of selected use ids in the viewmodel as well.
Until stateflow comes out, you'd need to provide the data in some other way
flows are stateless. They don't hold an internal value.
o

Orhan Tozan

03/19/2020, 7:36 PM
Check out my initial message, it already shows what I have
I already have a conflatedbroadcadtchannel for the selecteduserids
I guess onSaveButtonClick it should run sendNetworkCall(_selectedUserIds.value) ?
k

kevin.cianfarini

03/19/2020, 7:38 PM
okay I understand the question now. Call .value.
collect
is going to keep collecting the flow until it's closed. Also, collect wouldn't produce an element for you until another value is sent to the channel.
o

Orhan Tozan

03/19/2020, 7:40 PM
I understand
Thanks
What would StateFlow do?
k

kevin.cianfarini

03/19/2020, 7:41 PM
Stateflow would be similar to Androids livedata
you can post values to it and I believe read its current value
o

Orhan Tozan

03/19/2020, 7:58 PM
I see. So what if the view now wants to read the value of selectedUserIds? Sure it can collect, but let's say the view gets recreated, thus it needing to get the latest value again, what should the view do then?
k

kevin.cianfarini

03/19/2020, 7:59 PM
I'm sure you could subclass this flow such that every time it is collected, it re-emits the current value. This may already be implemented, I just haven't read much of the code.
o

Orhan Tozan

03/19/2020, 8:04 PM
You said thay Flow doesn't hold a value, but flow.first() is said to return its current value
k

kevin.cianfarini

03/19/2020, 8:05 PM
from the docs
The terminal operator that returns the first element emitted by the flow and then cancels flow’s collection. Throws NoSuchElementException if the flow was empty.
it will suspend until the first value is emitted to the flow
all it does is call collect under the hood and wait for the first value
no state is held in flows
o

Orhan Tozan

03/19/2020, 8:05 PM
So it's like collect, but stopping after it collected its first value
k

kevin.cianfarini

03/19/2020, 8:05 PM
correct
Why does he state it otherwise
k

kevin.cianfarini

03/19/2020, 8:07 PM
because this is how flow.first will operate on a DataFlow, not a normal flow
o

Orhan Tozan

03/19/2020, 8:07 PM
Yes. counter.first() does compute and return the current value. I'll mention it in the docs.
Ah I see
k

kevin.cianfarini

03/19/2020, 8:07 PM
He explicitly will call out that it operates differently than normal flow first
o

Orhan Tozan

03/19/2020, 8:08 PM
So if I want to expose a flow that holds a value, should I just recreate some sort of DataFlow myself, or perhaps copy paste it from the draft?
k

kevin.cianfarini

03/19/2020, 8:10 PM
You should stick with what you have
it does largely the same thing
I imagine there's a lot of edge cases which is why this proposal has been around since -September- July
o

Orhan Tozan

03/19/2020, 8:12 PM
But the thing is, this ViewModel will also be used by iOS views, and I assume in some case the iOS view would want to get direct access to the flow state, or am I just worrying about nothing?
k

kevin.cianfarini

03/19/2020, 8:12 PM
create a property on your vm which accesses the private channels value
Copy code
val value get() = channel.value