This is slightly related to my question <here>, bu...
# koin
j
This is slightly related to my question here, but is there away to scope non-viewmodel components to a nested nav graph? I’d ideally like to have an Interactor/Repo that exist for the entirety of a nav. graph but as soon as it’s last child route is popped from the app’s backstack I would like for it to be destoryed and a new instance returned if that nav. graph is routed too again.
u
can you create the repo (or have it injected from koin) etc. in the nav graph’s node and the pass it as parameter to the children?
j
I would like to have it injected from Koin but I’m unsure of how to scope it to a particular nav graph’s node. Do you have any experience doing this?
u
I have used nested NavHosts for thata but have also heared that this is an antipattern. Not sure if it can be achieved with plain navigation sub-graphs. As NavHosts are composables and live as long as they host children they are the perfect place to inject your viewmodel into. If you ran into the argument against nested navhost as well, please let me know. Still trying to figure out why that is supposed to be bad.
j
When you say nested NavHost do you mean a “nested graph” via the
NavGraphBuilder.navigation {}
extension function? I definitely think it’s an anti-pattern to use a NavHost inside of
NavGraphBuilder.composable {}
.
u
yes, the later. Why do you think it’s an antipattern.
j
NavGraphBuilder.navigation {}
should take care of creating a child nested graph that’s appropriately linked to the parent graph.. Using a nested NavHost would need its own controller. It’s completely separate from the parent Nav. Graph so you cannot route to it’s children.
u
Which might be exactly what you want (encapsulation)
and btw. you can if it let’s you. you can give it’s route parameters telling it what you want to do.
j
I definitely don’t want that. The nested graph is what I want, and have. My concern is have Koin construct objects only within it’s scope of the nested graph.
u
Then you need coin scoped viewmodel. but you need to manually manage them I think
j
I believe we are talking about two different things. I’m able to get my ViewModels to be scoped appropriately to a NavGraph now, what I want the Repositories viewmodels they depend upon to only be active while that nested graph is valid. Currently, using
factory {}
and
single {}
don’t work for me.
I have a few screens that each have their own VMs in this nested graph but I want them to all share access to the same repository.
u
don’t work for you, as you need to share the repository between viewmodels? I mean if you inject the repository into the viewmodel it will live ther for the scope of the viewmodel
got it
But
scoped
still seems right. Except you need to manage the scope manually. I.e. you need to find a hook to close the scope.
And again. As
navigation {}
is not a composable, it does not have a lifecycle. But you might be able to otherwise observe navigating out of your sub-graph.
j
I will try and figure out how the
koinViewModel()
works and replicate for any component.
u
it works because it lives inside a composable which has a composition’s life time.
That’s why I figured I need NavHost.
navigation{}
does not have a lifecycle.
j
It’s checking the ViewModelStoreOwner from the current BackStackEntry. According Ian Lake it should return content scoped to a graph.
u
Yes, it uses LocalViewModelStoreOwner.current for that
Which is again a composable function which is not available inside
navigation{}
but only inside the hierarchy of composables.
I’d rather go with
scoped
from koin and check if observing navController.currentBackStackEntryAsState gives you a hook to close the scope
j
That’s fine. I only inject my VMs to my destinations within a
composable {}
so that won’t be an issue.
koinViewModel()
is a Composable function so it would behave similarly to that; my issue now is getting scope objects that aren’t viewmodels.
u
Good luck, I need to leave for a long weekend trip now. tomorrow is a bank holiday here. If you care feel free to post here if you got it resolved.
m
recently i’ve written this article https://medium.com/proandroiddev/place-scope-handling-on-auto-pilot-with-koin-compose-navigation-d40023b1ba6f about injecting scoped dependencies below the viewModel and tying the lifetime of the scopes to the one of the nested nav graphs, it might help you with your use case.
❤️ 1
j
@Mihai Batista Do you have a sample project I can review? It’s not clear to me how to use your solution.
m
@Jonathan here is an example - https://github.com/mihbt/autoconnect-koin-scope . it showcases two app flows (each having two screens),
flow1
that uses dependencies scoped to the flow lifetime - here
FlowRepository
is scoped to the flow lifetime and discarded once the flow is left. and
flow2
that uses dependencies from the root scope. The composable that takes care of resolving the correct scope and pass it through CompositionLocal is https://github.com/mihbt/autoconnect-koin-scope/blob/main/app/src/main/java/com/example/koinscope/di/utils/AutoConnectKoinScope.kt and is plugged-in above the app navHost.
j
@Mihai Batista Thanks for providing the links. Looks pretty good. I will try playing with it in my app. I do have a question about this global variable. Should this be set to null in
onDispose {}
? If
AutoConnectKoinScope()
is ever removed from Composition it will no longer update lastKnownNavGraphRoute and if it is returned to the composition, it could have an inaccurate route. Not sure if this could cause subtle bugs.
m
@Jonathan i’ve had a closer look at that variable and it should not be cleared in
onDispose
. I see two cases when
AutoConnectKoinScope
leaves the composition, when there is a configuration change and when the process is killed due to memory pressure - in both cases when the activity is recreated, compose takes care of restoring the previous UI state, that’s why is important to keep around the last known nav route. I’ve spotted one issue with the global variable though - it will create issues in the second case, because the value is lost and the correct scope cannot be resolved. That’s why I’ve updated implementation to store the last known state in the activity bundle instead to survive the process death - the update is in this file https://github.com/mihbt/autoconnect-koin-scope/blob/main/app/src/main/java/com/example/koinscope/di/utils/AutoConnectKoinScope.kt
i would be happy to hear your results with integrating it in your app
a
interesting case @Mihai Batista 👍 I will read your article
m
@arnaud.giuliani awesome, thanks!