I'm having issues trying `@Inject lateinit var` fr...
# android
p
I'm having issues trying
@Inject lateinit var
from official android
Hilt
documentation: https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419 I'm doing the same they show in their lateinit var sample:
Copy code
class MyModelViewModel() : ViewModel() {
    @Inject lateinit var myModelRepository: MyModelRepository
But that gives me this exception:
kotlin.UninitializedPropertyAccessException: lateinit property myModelRepository has not been initialized
If I try adding
@HiltViewModel
on class
MyModelViewModel()
then I got this error:
error: [Hilt] @HiltViewModel annotated class should contain exactly one @Inject or @AssistedInject annotated constructor.
a
yes, your view model must have annotation
@HiltViewModel
and then you can pass your repository via parameter in constructor. Note, that MyModelRepository has to be provided: either using a provider method or constructor injection.
Copy code
@HiltViewModel
class MyModelViewModel(
    private val myModelRepository: MyModelRepository 
) : ViewModel()
for MyModelRepository option1, smth like this
Copy code
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {

    @Provides
    fun provideMyDependency(): MyModelRepository {
        return MyModelRepository()
    }
}
options2:
Copy code
class MyModelRepository @Inject constructor() {...}
p
@Andrei Mandychev Constructor injection? why? I'm trying to do exactly the opposite
I'm trying to do field injection
@Inject lateinit var
from official android
Hilt
documentation: https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419
it is done using "@Inject lateinit var" instead of parameter injection, they uses it on their sample, but their sample is not a viewmodel.
do you mean is impossible to use field injection on viewmodels?
@ephemient I can't see the use of lateinit var injection on your link
e
why do you want to do field injection?
p
I prefeer to use that in some circumstances, for example, when I wanna pass non hilt parameters on a constructor, I need to avoid constructor injection, because I don't feel confortable with assisted injection on constructors, it adds a lot of boilerplate and complexity that can be avoided using simply normal constructors and field injection
e
well Dagger still has to get involved somewhere,
@Inject lateinit var
doesn't magically populate itself. so you may not call the constructor yourself. (that's the whole thing with dependency injection)
p
ummm, but google is showing a sample in their documentation in which they simply use @Inject lateinit var to receive a injected field
e
yes. but the ViewModel still needs an
@Inject constructor
which you don't call, you get the object injected to you.
p
Also, if constructor injection doesn't need a manual injection, why field injection should need it?
I dont understand your last message
e
Copy code
@HiltViewModel
public class MyViewModel @Inject constructor() extends ViewModel {
    @Inject
    lateinit var dependency: Dependency
}
Copy code
val viewModel = MyViewModel() // Dagger is not involved at all, dependency is still unset

val viewModel: MyViewModel by viewModels() // Dagger constructs and injects
p
@ephemient the problem if that if I do that... I can't pass parameters to the viewmodel constructor, because it is using hilt injection in the constructor, so it forces me to do assisted injection, which is what I'm trying to avoid
e
there is another https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-factories approach
Copy code
@HiltViewModel
public class MyViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
) extends ViewModel {
    val id: Int = savedStateHandle["id"]
}
Copy code
val viewModel: MyViewModel by viewModels(
    extrasProducer = {
        MutableCreationExtras(defaultViewModelCreationExtras).apply {
            set(DEFAULT_ARGS_KEY, bundleOf("id" to id))
        }
    },
)
but it's really more work than just using assisted inject
p
yep, thank you, so as I can see, is not possible to use inject fields on viewmodels
so if I wanna go with Hilt, I'm forced to use inject constructor
and assisted inject
e
it is possible as long as you let Dagger do the construction also
but there's no reason to do field injection when you can do constructor injection
p
I see that I will need to accomodate to assisted injection for passing non hilt parameters
I really don't like it
e
the whole thing with dependency injection is that you don't construct objects yourself, they are provided to you by something else which knows the details about what to construct
p
ofc, but when you need to pass a parameter, for example, a simple string, it forces you to do it with assisted injection, making it tedious and forcing you to add boilerplate
but well, as is the way to proceed, I'll need to accomodate to it
p
do you think that if they listen to that issue it will be possible to use field injection in viewmodel without inject constructor and assisted injection?
e
I don't know why you're still stuck on that. Dagger needs to be involved in order to perform injection
there's a bit more boilerplate to use assisted injection than is necessary and that is something that can be resolved though
p
Having a class like this:
Copy code
@AndroidEntryPoint 
class CustomClass @Inject constructor (val repository: Repository, val name: String) {
Let's say I only want to inject the repository parameter, but I want to pass the name parameter during initialization like so:
Copy code
val customClass = CustomClass("name")
with
@Inject constructor
I'm forced to add a lot of boirlerplate and complexity:
Copy code
@AndroidEntryPoint 
class CustomClass @AssisgtedInject constructor (
    val repository: Repository, 
    @Assisted val name: String
) {

    @AssistedFactory
    interface CustomClassFactory {
        fun create(name: String): CustomClass
    }
}
To add more boilerplate and useless complexity, that forces you to create the
CustomClass
class with the
interface
instead of the normal
class
, and calling the
create
method. On the other hand, if you instead do it using
@Inject lateinit var
, is that simple:
Copy code
@AndroidEntryPoint 
class CustomClass(val name: String) {
    @Inject lateinit var repository: Repository
}
Now, think that this is a super simple example with just one non Hilt parameter, but this can get a lot more big as you know.
I search simplicity when developing, I never used DI frameworks until now, and I'm trying to feel confortable when using Hilt, assisted injection feels not confortable for me
e
sure, but there's no way you can use
CustomClass(...)
directly for reasons specified previously. so it is handled by a factory method instead, and that needs to be in an interface so that Dagger can implement it. there's nothing limiting you from putting multiple factory methods into the same interface though, if you wanted to amortize that
h
@Pablo you should using koin
p
why @Huixing.Wang ? supposedly Hilt is the recomended way in Android
h
Because koin can do the things you want
p
can you please give me a sample of how will it help me with my sample?
Having a class like this:
Copy code
class CustomClass(val repository: Repository, val name: String) {
Let's say I only want to inject the repository parameter, but I want to pass the name parameter during initialization like so:
Copy code
val customClass = CustomClass("name")
how will achieve it with koin?
e
as far as I'm aware, koin can do parameter injection in a similar manner (extra lambda configuring params on the injector) but it's not compile-time checked. I haven't used it though.
189 Views