https://kotlinlang.org logo
#decompose
Title
# decompose
a

Ahmad Hassan

03/20/2023, 12:51 PM
Hi, if we consider Component as a ViewModel, what is the recommended approach to have a viewModelScope inside component to launch coroutines?
a

Arkadii Ivanov

03/20/2023, 1:09 PM
You can create a scope and attach it to the lifecycle. See the docs - https://arkivanov.github.io/Decompose/component/scopes/
a

Ahmad Hassan

03/20/2023, 5:34 PM
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

Arkadii Ivanov

03/20/2023, 5:37 PM
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

Ahmad Hassan

03/20/2023, 5:38 PM
Well, in my case hosting component is my root component.
That’s why, it'll not be destroyed
a

Arkadii Ivanov

03/20/2023, 5:39 PM
Which platform are you talking about?
a

Ahmad Hassan

03/20/2023, 5:40 PM
Android
a

Arkadii Ivanov

03/20/2023, 5:40 PM
On Android it should be destroyed when the hosting activity or fragment is destroyed
a

Ahmad Hassan

03/20/2023, 5:43 PM
I meant it’s fine across configuration changes. But the component will not be destroyed when the screen closes (when back pressed.)
a

Arkadii Ivanov

03/20/2023, 5:44 PM
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

Ahmad Hassan

03/20/2023, 5:47 PM
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

Arkadii Ivanov

03/20/2023, 5:48 PM
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

Ahmad Hassan

03/20/2023, 5:49 PM
Yeah, I understand this.
a

Arkadii Ivanov

03/20/2023, 5:50 PM
So the root component is only destroyed when the hosting activity/fragment itself is destroyed.
a

Ahmad Hassan

03/20/2023, 5:50 PM
That’s why I’m asking the approach to act my component as viewModel
a

Arkadii Ivanov

03/20/2023, 5:51 PM
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

Ahmad Hassan

03/20/2023, 5:53 PM
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

Arkadii Ivanov

03/20/2023, 5:54 PM
You are using component factories incorrectly
Component instances must never be placed inside InstanceKeeper.
a

Ahmad Hassan

03/20/2023, 5:57 PM
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

Arkadii Ivanov

03/20/2023, 6:00 PM
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

Ahmad Hassan

03/20/2023, 6:03 PM
As my current approach is wrong, what would be the correct one for my use case?
a

Arkadii Ivanov

03/20/2023, 6:04 PM
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

Ahmad Hassan

03/20/2023, 6:24 PM
I was checking exactly this. Let’s see. Thank you for your time.
a

Arkadii Ivanov

03/20/2023, 6:26 PM
No problem at all! The idea is to use InstanceKeeper inside the child component. Don't place the component itself into InstanceKeeper.
👍 1
7 Views