Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Hilt AssistedInject #271

Merged
merged 4 commits into from
Dec 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ kotlin = "1.9.10"
kodein = "7.20.2"
koin = "3.4.3"
koin-compose = "1.0.4"
hilt = "2.47"
hilt = "2.49"
leakCanary = "2.9.1"
appCompat = "1.6.1"
lifecycle = "2.6.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ data class HiltDetailsScreen(

// Uncomment version below if you want keep using ViewModel instead of to convert it to ScreenModel
// ViewModelProvider.Factory is not required. Until now Hilt has no support to Assisted Injection by default
/*val viewModel: HiltDetailsViewModel = getViewModel(
viewModelProviderFactory = HiltDetailsViewModel.provideFactory(index)
)*/
// val viewModel: HiltDetailsViewModel = getViewModel<HiltDetailsViewModel, HiltDetailsViewModel.Factory> { factory -> factory.create(index) }

// This version include more boilerplate because we are simulating support
// to Assisted Injection using ScreenModel. See [HiltListScreen] for a simple version
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package cafe.adriel.voyager.sample.hiltIntegration

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel

// Until now, there is no support to assisted injection.
// Follow the thread here: https://github.com/google/dagger/issues/2287
class HiltDetailsViewModel(
val index: Int
@HiltViewModel(assistedFactory = HiltDetailsViewModel.Factory::class)
class HiltDetailsViewModel @AssistedInject constructor(
@Assisted val index: Int
) : ViewModel() {
companion object {
fun provideFactory(
index: Int
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T = HiltDetailsViewModel(index) as T
}

@AssistedFactory
interface Factory {
fun create(index: Int): HiltDetailsViewModel
}

}
46 changes: 46 additions & 0 deletions voyager-hilt/src/main/java/cafe/adriel/voyager/hilt/ViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.hilt.internal.componentActivity
import dagger.hilt.android.lifecycle.withCreationCallback

/**
* A function to provide a [dagger.hilt.android.lifecycle.HiltViewModel] managed by voyager ViewModelLifecycleOwner
Expand Down Expand Up @@ -50,3 +51,48 @@ public inline fun <reified T : ViewModel> Screen.getViewModel(
provider[T::class.java]
}
}

/**
* A function to provide a [dagger.hilt.android.lifecycle.HiltViewModel] managed by voyager ViewModelLifecycleOwner
ghostbear marked this conversation as resolved.
Show resolved Hide resolved
* instead of using Activity ViewModelLifecycleOwner.
* There is compatibility with Activity ViewModelLifecycleOwner too but it must be avoided because your ViewModels
* will be cleared when activity is totally destroyed only.
*
* @param viewModelProviderFactory A custom factory commonly used with Assisted Injection
* @param viewModelFactory A custom factory to assist with creation of ViewModels
* @return A new instance of [ViewModel] or the existent instance in the [ViewModelStore]
*/
@Composable
public inline fun <reified VM : ViewModel, VMF> Screen.getViewModel(
viewModelProviderFactory: ViewModelProvider.Factory? = null,
noinline viewModelFactory: (VMF) -> VM,
ghostbear marked this conversation as resolved.
Show resolved Hide resolved
) : VM {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
}
return remember(key1 = VM::class) {
val hasDefaultViewModelProviderFactory = requireNotNull(lifecycleOwner as? HasDefaultViewModelProviderFactory) {
"$lifecycleOwner is not a androidx.lifecycle.HasDefaultViewModelProviderFactory"
}
val viewModelStore = requireNotNull(viewModelStoreOwner?.viewModelStore) {
"$viewModelStoreOwner is null or have a null viewModelStore"
}

val creationExtras = hasDefaultViewModelProviderFactory.defaultViewModelCreationExtras.withCreationCallback(viewModelFactory)

val factory = VoyagerHiltViewModelFactories.getVoyagerFactory(
activity = context.componentActivity,
delegateFactory = viewModelProviderFactory ?: hasDefaultViewModelProviderFactory.defaultViewModelProviderFactory
)

val provider = ViewModelProvider(
store = viewModelStore,
factory = factory,
defaultCreationExtras = creationExtras
)

provider[VM::class.java]
}
}