How to scope an object instance to Jetpack Compose navigation graph lifecycle using Hilt?

272 Views Asked by At

Let's assume I have a multimodule Jetpack Compose project with feature modules and I want to scope some object instances to features lifecycles. For example, I want to scope AuthRepo instance to AuthFeature lifecycle and delete it from memory right after user logged in. Let's also assume that my feature consists of a few composable screens.

So, my MainActivity looks like this:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ...
            NavHost(navController = navController, startDestination = *AuthFeature graph*) {
               navigation(*AuthFeature graph*) {
                   composable(*First Screen*) { backStackEntry ->
                       val authGraphEntry = *get authGraphEntry*
                       val vm = hiltViewModel<AuthViewModel>(authGraphEntry)
                       ...
                   }

                   composable(*Second Screen*) { backStackEntry ->
                       val authGraphEntry = *get authGraphEntry*
                       val vm = hiltViewModel<AuthViewModel>(authGraphEntry)
                       ...
                   }
              }

              *Other feature graph with multiple screens*
              
         }
    }    

In such case there's no problem: AuthViewModel, which is common for both screens, is scoped to AuthFeature graph and I can use Hilt's @ViewModelScoped annotation while injecting AuthRepo. So AuthViewModel will be destroyed after navigating to the other feature (with popping up to the root) and AuthRepo is going to be destroyed too.

The problem appears when I decide to create one ViewModel per screen. The first screen and the second screen consider to use the same instance of AuthRepo, however annotating AuthRepo with @ViewModelScoped leads to creating two instances of AuthRepo as I have 2 ViewModels. Annotating it with @Singleton leads to creating one single instance for the whole app, which is not going to be removed after navigating to another feature.

I came up with 2 ways to solve the issue:

  1. Wrap every feature in an activity and use @ActivityRetainedScoped. However I want to use SingleActivity pattern in my project so this solution is not valid for me.

  2. Use Dagger and its custom scopes. But I'd like to have a simpler solution without manual component lifecycle handling to keep code cleaner and less error-prone.

On the other hand, I don't want to leave 1 ViewModel per feature because one day it can grow to an enormous amount lines of code. Any ideas? Please correct me if I'm wrong in some of my statements.

0

There are 0 best solutions below