https://kotlinlang.org logo
#dagger
Title
# dagger
b

blakelee

10/28/2020, 4:48 AM
Is there way to inject viewmodels without having to create a factory for each one and also without hilt? I don’t need savedStateHandle or anything, I just want to use it like @Inject but have it survive configuration changes that a normal class won’t survive. I’ve seen several medium articles but I haven’t found a solution that can do it without a factory or hilt
a

allan.conda

10/28/2020, 7:10 AM
Copy code
@Binds
@IntoMap @ViewModelClassKey(LoginViewModel::class)
abstract fun bindViewModel(viewModel: SomeViewModel): ViewModel
look for tutorials involving ViewModelClassKey
a

Ahmed Ibrahim

10/28/2020, 9:46 AM
Yes, there is, since you don't use SavedStateHandle you can do it by using a Provider.
Copy code
@Inject
lateinit var viewModelProvider: Provider<MyViewModel>

private val myViewModel by viewModels {
    viewModelProvider.get()
}
b

blakelee

10/29/2020, 1:41 AM
@allan.conda That just replaces the factory with a binds @Ahmed Ibrahim The viewModelProvider.get() doesn't seem to work. viewModels wants a ViewModelProvider.Factory
a

allan.conda

10/29/2020, 1:41 AM
Yeah… you said no Factory or Hilt… That’s the plain Dagger solution
b

blakelee

10/29/2020, 1:43 AM
I was hoping there was a way to set it up with just @Inject like many of my other classes. I thought there was a way to do it similar to what Ahmed was suggesting, but I must be mistaken.
a

allan.conda

10/29/2020, 1:45 AM
using Hilt is the closest to what you’re looking for with
@ViewModelInject
otherwise there’s no easy way since ViewModels are a corner case,
a

Ahmed Ibrahim

10/29/2020, 8:54 AM
@blakelee Hey, sorry for providing an incomplete code, but yeah it's missing the ViewModelProvider.Factory as you said, but you can get around that by doing
Copy code
@Inject
    lateinit var viewModelProvider: Provider<MyViewModel>

    private val myViewModel: MyViewModel by viewModels {
        object: ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                @Suppress("UNCHECKED_CAST")
                return viewModelProvider.get() as T
            }
        }
    }
Now you can replace the ugly
object: ViewModelProvider.Factory
part by extracting it to an inline function to get a much more elegant version of the above code
Copy code
@Inject
    lateinit var viewModelProvider: Provider<ViewModel>

    private val myViewModel: ViewModel by viewModels {
       createSimpleViewModelFactory {        viewModelProvider.get() 
}
    }
b

blakelee

10/29/2020, 6:32 PM
@Ahmed Ibrahim This seems like it could work. It's running on my device just fine. Just gotta make sure it really does handle config changes. Thanks!
8 Views