Hi, if we consider Component as a ViewModel, what ...
# decompose
a
Hi, if we consider Component as a ViewModel, what is the recommended approach to have a viewModelScope inside component to launch coroutines?
a
You can create a scope and attach it to the lifecycle. See the docs - https://arkivanov.github.io/Decompose/component/scopes/
a
Thanks for the reply. How can my components act as ViewModels? (must be destroyed when the screen is closed and retained across configuration changes in Android ) If I use instancekeeper, it survives across configuration changes but won’t be destroyed on screen close.
a
You should use InstanceKeeper, instances stored there should be destroying when the hosting component is destroyed permanently
Just make sure that you are using IntanceKeeper provided by the component's ComponentContext
a
Well, in my case hosting component is my root component.
That’s why, it'll not be destroyed
a
Which platform are you talking about?
a
Android
a
On Android it should be destroyed when the hosting activity or fragment is destroyed
a
I meant it’s fine across configuration changes. But the component will not be destroyed when the screen closes (when back pressed.)
a
This should work exactly like this. Could you please show the code how you integrate the root component?
Ahh, on new android devices the activity is not destroyed when you press back button. It's just minimized instead. You might be experiencing this behaviour.
a
Let me clarify. My App is Compose Multiplatform, so the component should also be destroyed when composable screen is destroyed. How can I achieve this?
a
This is not true. The root component is tied to the activity lifecycle and its ViewModelStore
Compose lifecycle has no effect on component lifecycle.
a
Yeah, I understand this.
a
So the root component is only destroyed when the hosting activity/fragment itself is destroyed.
a
That’s why I’m asking the approach to act my component as viewModel
a
Component itself never acts as ViewModel, but its InstanceKeeper does.
So if you integrate the root component properly (as in the docs), and your hosting activity is destroyed (onDestroy is called), and there is no configuration change, then InstanceKeeper is destroyed as well
Component <-> Fragment, InstanceKeeper <-> ViewModel
a
This is my RootComponent
Copy code
class NoteRootComponent(
    componentContext: ComponentContext,
) : ComponentContext by componentContext, KoinComponent {

    private val navigation = StackNavigation<Config>()

    private val _stack = childStack(
        source = navigation,
        initialConfiguration = Config.List,
        handleBackButton = true,
        childFactory = ::createChild
    )

    val stack: Value<ChildStack<*, Child>> = _stack

    private fun createChild(config: Config, componentContext: ComponentContext): Child {
        return when (config) {
            is Config.List -> Child.List(noteList(componentContext))
            is Config.Details -> Child.Details(noteDetail(config))
        }
    }

    private fun noteList(componentContext: ComponentContext) =
        NoteListViewModelComponent(componentContext, noteDataSource = get()) {
            navigation.push(Config.Details(it))
        }

    private fun noteDetail(config: Config.Details): NoteDetailViewModelComponent {
        return instanceKeeper.getOrCreate {
            NoteDetailViewModelComponent(
                Dispatchers.Main, noteDataSource = get(),
                noteId = config.id
            ) {
                navigation.pop()
            }
        }
    }

    sealed class Child {
        class List(val component: NoteListViewModelComponent) : Child()
        class Details(val component: NoteDetailViewModelComponent) : Child()
    }

    private sealed class Config : Parcelable {
        @Parcelize
        object List : Config()

        @Parcelize
        data class Details(val id: Long?) : Config()
    }
}
a
You are using component factories incorrectly
Component instances must never be placed inside InstanceKeeper.
a
Actually, use case is, I want shared ViewModel across Android and iOS.
I don’t want to use Separate ViewModel/implementation for each platform.
a
Let's clarify on the terminology in Decompose (by design). A component - is an entity similar to Fragment. It is never retained. InstanceKeeper - is an entity similar to ViewModelStore. InstanceKeeper.Instance - is similar to ViewModel.
You can't have retained component by design
You can share your comment, and use InstanceKeeper internally to retain instances (aka ViewModels)
a
As my current approach is wrong, what would be the correct one for my use case?
a
I think just share your root component! Use InstanceKeeper internally in every component where you need to retain instances over configuration changes on Android.
Like in all samples
a
I was checking exactly this. Let’s see. Thank you for your time.
a
No problem at all! The idea is to use InstanceKeeper inside the child component. Don't place the component itself into InstanceKeeper.
👍 1
f
@Arkadii Ivanov I have tried for a long time integrate Decompose Navigation with my viewmodels traditionals, but when I navigato to other screen and then back, seems this viewmodel hasn't destroyed, I have tried this because only I would like have the back-gesture on iOS; due to Compose Navigation not support this back-gesture; do you have any sample to do it? really, I haven't been able yet. thanks.
a
Are you trying to use reuse your existing AndroidX ViewModels? Decompose encourages different architecture without ViewModels. So ideally you would need to replace your every ViewModel with a class that just implements
InstanceKeeper.Instance
interface and use
InstanceKeeper
in the hosting component. But if that's not possible, you can try https://arkivanov.github.io/Decompose/tips-tricks/composable-viewmodel/
f
Yes, I have tryed to reuse AndroidX Viewmodels and use Koin for inject one; So, Do you recomend me that use https://arkivanov.github.io/Decompose/tips-tricks/composable-viewmodel/ ? Maybe you have any sample doing this? Thanks @Arkadii Ivanov
In fact, I've tryed integrate my viewmodels without modifiy them; but when I navigate from HomeScreen to DetailScreen, DetailScreen ever hold and keeps in memory the first element, then I navigate to back and try to go to Detail again...and I see the same, it ever remember the first element for all live cycle; in fact, decompose not destroy the viewmodels instance when I'm not there; if you have any other sample, it will be helpfully for ours. Thanks! @Arkadii Ivanov
a
The link I provided contains sample code. If you have any issues, it would be nice to see a reproducer or at least some code.