Is it better to use LiveData in a ViewModel or use...
# compose
y
Is it better to use LiveData in a ViewModel or use
Copy code
by remember { mutableStateOf(X) }
Using remember takes less time but I am not sure if its recommended over livedata? Although I am not sure if it matters in the end of the day at all or not
j
by mutableStateOf(X)
or
Flow<T>.collectAsState()
are both preferred over LiveData and/or AAC ViewModel for exactly the reason mentioned in the previous comment.
πŸ™Œ 1
a
I'd avoid LiveData in new code, with or without compose.
πŸ‘† 3
As for avoiding AAC ViewModel, imo there are places for it but keeping your dependencies simpler generally pays off, and AAC VM is a decently complicated dependency. I don't share the blanket dislike for platform dependent code, but I do think that it's always worth considering if any dependencies, platform or otherwise, really need to be there.
Hoistable state is always useful; `remember {}`ing each incremental piece of observable state can get unwieldy and that state can only exist as part of composition. Declaring a class with properties
by mutableStateOf(...)
can give you a lot of the benefits associated with AAC ViewModel without actually subclassing
ViewModel
to do it or introducing that dependency.
πŸ‘† 1
πŸ’― 1
y
Thanks for the explanation. I will avoid using them from now on.
a
I've also seen some very nicely factored code where an AAC ViewModel holds instances of several other smaller and simpler hoisted state objects that are passed to other composable functions, limiting the scope of ViewModel as a dependency
πŸ‘† 1
Sometimes at the layer of a screen or similar
y
I am just in general unsure about how I should structure my code in Compose. So I should avoid using ViewModels and LiveData? Then it wont be MVVM?! I can use the remember stuff for most of the parts but there are like 2 lists in my app that need to be accessed from many different places so I am not sure how I can let the whole app access them without using Viewmodels. Currently I have a MainViewModel which provides these lists to the whole app. And a few simpler viewmodels for screen specific needs which can easily be converted to remember.
I actually thought using viewmodels was a better choice so I decided to use it. It felt more clean I guess. But I guess I was wrong.
I have also tried providing a repository to the whole app by putting it in MainActivity and accessing it using the activity whenever needed, but I dont think thats a good idea as well. I would usually just interact with repos in viewmodels but if I remove viewmodels, how would I use repos? I looked into injecting them using compose nav but I couldnt find any compose hilt docs for anything other than viewmodels.
a
So I should avoid using ViewModels and LiveData? Then it wont be MVVM?!
MVVM is just a pattern, you don't need AAC
ViewModel
and
LiveData
types to use the pattern. If you feel more comfortable using those types, you can, but you can often express things a bit more clearly/tersely in compose without them.
i
If you support configuration changes of any type, you really do what to be using AAC ViewModels at some level to cache data across config changes (as saved state, the only other thing that survives config changes, is only for small keys). To Adam's point, that doesn't mean your composables need a hard reference to the ViewModel itself - it can just be a holder for regular old Flows and State
πŸ‘† 2
a
right. To be really pedantic, you don't need to use
ViewModel
to cache data across config changes, but it's probably the simplest way to do so.
I personally like keeping logic out of my
ViewModel
subclasses entirely and just use them to reference other objects that I need to marshal across those config changes, putting the smarts in those other objects instead
but that's more a matter of taste than anything else
You can do just about everything you would do with `ViewModel`/`LiveData` in compose by removing the
: ViewModel()
from your class declaration and changing property pairs like this:
Copy code
private val _myData = MutableLiveData<MyData>(...)
val myData: LiveData<MyData>
  get() = _myData
with:
Copy code
var myData by mutableStateOf(...)
  private set
πŸ‘€ 1
and then just use it like a variable instead of fussing about with
setValue
vs.
postValue
based on what thread you're on, or explicitly observing it vs. just letting snapshot observation do the work for you
πŸ‘€ 1
y
Yeah I guess that could work for most of the stuff, although I am not sure how I can use coroutines after removing
: ViewMode()
. It doesnt look like I can use them on my composables as well, I guess I would need viewmodels for those functions? My main problem is that literally every single screen of my app needs to use my
SettingsRepository
. I am working on a launcher so every single thing on the UI is based on the users configuration. Without Compose I would just create a viewmodel for each activity/fragment and let the viewmodel talk to the repository but using compose I am just injecting the repo into my activity and using it everywhere I need that way without having to create new viewmodels for each screen. I have to send my activity as a param to most of the main composables to do this and even tho it is super easy to use it this way I feel like its not a good way of handling it.
a
I'll preface this with if
ViewModel
is working for you for various reasons, by all means keep using it. Compose doesn't mind one way or the other. πŸ™‚
😊 1
But prior to compose, when writing Android apps there were a lot of things that AAC
ViewModel
made drastically easier, and with compose, it's not the only way available to accomplish many of those things anymore
regarding coroutine scoping, I regret that we shipped the
viewModelScope
extensions at all; I think probably 95% of usages of it are better suited for either the UI scope (activity, fragment, compose, whatever) or a repository-level scope
πŸ‘ 1
but the above is very much an opinion, not a hard and fast law by any stretch πŸ™‚
I think
viewModelScope
is an example of AAC
ViewModel
being a local maximum for a lot of things. It's almost always within reach and there's a way to do a lot of things with it, but it pulls along a lot of its own quirks, dependencies, and lifecycle concerns that often pull other things into its gravity well that may work better outside of it
for a lot of codebases it became the same kind of monolithic catch-all that fragment and activity subclasses became in years prior; better in some ways but still kind of shuffling the same issues around
y
Thanks. My code works fine now I guess, but since its a large and complex app I just want to start it the best way possible so wont have to do lots of refactoring later on. Although I don't think its avoidable for anyone anyways.
Thanks for all the information & your time! πŸ˜„ πŸ™
πŸ‘ 1
d
@YASAN Have a look at the D-KMP sample, which doesn't use the AAC ViewModel, and where the different screens have access to the same Repository. It also works out-of-the-box with SwiftUI on iOS: https://github.com/dbaroncelli/D-KMP-sample
y
ooo Thanks ill take a look
j
If we do not use AAC
ViewModel
- are there other possibilities to have
backstackEntry
viewModel scoping together with compose navigation? Like when we navigate to sub screens, we'd like to keep the current viewmodel to cache state or avoid reloading lists of data when the user goes back. I don't see why you want to give up the integration of compose-navigation, AAC ViewModels and
SavedStateHandle
and I can't see how you would easily replace this without using AAC ViewModels and
viewModel()
.
πŸ‘€ 1