I have a button click in my composable, that needs...
# compose
c
I have a button click in my composable, that needs to run a suspend function to get some text... and then I want to copy that text to clipboard (which requires an activity reference)
Copy code
val myButtonLaunch = {
        val myText = viewModel.getAsyncData()
        MyClipboardService.copyIntoClipboard(myText)
}
The above doesn't run because the button click lambda isn't a coroutine scope. What are my options here? Pass activity into
getAsyncData()
and inside of getAsyncData I copyIntroClipboard?
k
Pass in a lambda that will update the clipboard. Same as for any event handling in compose
c
hm. TIL! I thought passing in a lambda from my composable to my viewmodel was frowned upon.
Gonna convert to this
Copy code
val myButtonLaunch = {
        viewModel.getAsyncDataAndCopy { text -> MyClipboardService.copyIntoClipboard(text)
   }
}
k
You're not passing it to the model. As the result of a user interaction, you're bubbling that info up to the place that will handle the result of the interaction.
i
c
@Kirill Grouchnikov I swear I asked something similar a few months ago and the concensus was that that was bad. Let me look up that thread. maybe im misremembering things
@Ian Lake I did! But I also thought that maybe that's bad because there's a chance that my viewModel doesn't get the text soon enough (lets say like 5 seconds) but then the user rotates their device that the callback wouldn't get delivered.
but i guess saying that out loud makes it sound like it makes no sense. remember* is there for a reason.
hm.
I guess out of the two choices here... none are better than the other, right?
i
It would be a memory leak in that 'passing a lambda to your ViewModel case' if you rotate your device
c
Oh, so yeah, Kirill's approach would be a mem leak... right? That's what I thought as of a discussion like a few months ago
i
Depends on whether the lambda captures anything that directly or indirectly references anything tied to the activity
c
In my case... it would because the clipboard manager is obtained using activity's context?
i
Yeah, it would be in that case
The important part is to think about it in terms of cancellation and resuming: Should it cancel if the user rotates their device? If yes,
rememberCoroutineScope
is perfect for you since it does that cancellation for you. If no, then you already need to have a pipeline to have your ViewModel call update some state that you can read in the new instance of your UI
c
Okay. Yeah. So like... another way to think about it is that since the lambda captures the activity context... that's pretty much the same mem leak that would've been caused by my opening question. "Pass activity into
getAsyncData()
and inside of getAsyncData I copyIntroClipboard?" That would also be a mem leak?
ye, I don't care if the network call gets cancelled on rotation. rememberCoroutineScope it is!
im going to go print your last message and put it on my wall, Ian lol Thanks!
f
I'll chime in to add that, as a rule of thumb, you should not expose
suspend
methods from your viewmodel to call from the UI. The viewmodel should expose a regular method that returns quickly and all it does is push an event to a queue, to be processed asynchornously
i
...unless you want it's lifetime to be tied to the existence of the UI and cancelled when the UI disappears. That's kind of the whole point of structured concurrency of suspend methods
f
hence
rule of thumb
c
Thanks everyone. I think it's all starting to make more sense now. 😄