We're migrating to use the Hilt ViewModel integration and are hitting a possible edge case. We have a BasePresenter class that uses field injection to avoid our presenters having to pass through a bunch of args into the super constructor:
open class BasePresenter {
@Inject lateinit var foo: Foo
}
class SomeScreenPresenter @Inject constructor(val dependency: Dependency) : BasePresenter() {...}
When we inject these using vanilla dagger/hilt, the SomeScreenPresenter_Factory does field injection in its get() method:
@Override
public SomeScreenPresenter get() {
SomeScreenPresenter instance = newInstance(dependencyProvider.get())
BasePresenter_MembersInjector.injectFoo(instance, fooProvider.get());
return instance;
}
But when migrating to use the Hilt ViewModel integration, the generated ViewModelAssistedFactory doesn't do any field injection:
@Override
@NonNull
public SomeScreenPresenter create(SavedStateHandle arg0) {
return new SomeScreenPresenter(dependency.get());
}
This may be a weird use-case, but thought I'd see if there's a technical reason this isn't supported, or if maybe it was just an omission.
Thanks!
This is not a weird case and its actually very useful for libraries for the reason you mentioned (not having to pass deps through a bunch of args into constructor). This happens because @ViewModelInject has its own assisted factory that is created via an @Provides methods and those don't do member injection.
We are in the process of supporting this along with a ViewModelComponent but it will require an API change, the idea is to have developers use familiar Dagger APIs along with an additional Hilt one, something like this:
@HiltViewModel // Name TBD
class MyViewModel @Inject constructor(SavedStateHandle handle, FooRepository foo): BaseViewModel() {
// ...
}
Under the hood Dagger will generate the factory that will do member injection instead of having a dedicate one for ViewModels that replicate's Dagger. You won't need to annotate SavedStateHandle with an assisted annotation since it'll be a @BindInstance in the ViewModelComponent and we can have additional checks to disallow accidental injections of the ViewModel (i.e. @Inject vm: MyViewModel).
WorkManager's Workers will have a similar change.
I'll leave this issue open until this is supported.
Most helpful comment
This is not a weird case and its actually very useful for libraries for the reason you mentioned (not having to pass deps through a bunch of args into constructor). This happens because
@ViewModelInjecthas its own assisted factory that is created via an@Providesmethods and those don't do member injection.We are in the process of supporting this along with a
ViewModelComponentbut it will require an API change, the idea is to have developers use familiar Dagger APIs along with an additional Hilt one, something like this:Under the hood Dagger will generate the factory that will do member injection instead of having a dedicate one for ViewModels that replicate's Dagger. You won't need to annotate
SavedStateHandlewith an assisted annotation since it'll be a@BindInstancein theViewModelComponentand we can have additional checks to disallow accidental injections of the ViewModel (i.e.@Inject vm: MyViewModel).WorkManager's Workers will have a similar change.
I'll leave this issue open until this is supported.