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