For ViewModels
which has only compile-time dependencies, I use the ViewModelProvider.Factory
from Architecture components like following:
class ViewModelFactory<T : ViewModel> @Inject constructor(private val viewModel: Lazy<T>) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T = viewModel.get() as T
}
And in my Activity
or Fragment
I get the ViewModel
in following way:
@Inject
lateinit var viewModelFactory: ViewModelFactory<ProductsViewModel>
This is working fine until my ViewModel
needs a dependency which is only available at run-time.
Scenario is, I have a list of Product
which I am displaying in RecyclerView
. For each Product
, I have ProductViewModel
.
Now, the ProductViewModel
needs variety of dependencies like ResourceProvider
, AlertManager
etc which are available compile-time and I can either Inject
them using constructor
or I can Provide
them using Module
. But, along with above dependencies, it needs Product
object as well which is only available at run-time as I fetch the list of products via API call.
I don't know how to inject a dependency which is only available at run-time. So I am doing following at the moment:
ProductsFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
productsAdapter = ProductsAdapter(context!!, products, R.layout.list_item_products, BR.productVm)
rvProducts.layoutManager = LinearLayoutManager(context)
rvProducts.addItemDecoration(RecyclerViewMargin(context, 10, 20))
rvProducts.adapter = productsAdapter
getProducts()
}
private fun getProducts() {
productsViewModel.getProducts()
.observe(this, Observer { productResponse: GetProductResponse ->
products.clear()
productsAdapter?.notifyDataSetChanged()
val productsViewModels = productResponse.data.map { product ->
// Here product is fetched run-time and alertManager etc are
// injected into Fragment as they are available compile-time. I
// don't think this is correct approach and I want to get the
// ProductViewModel using Dagger only.
ProductViewModel(product, resourceProvider,
appUtils, alertManager)
}
products.addAll(productsViewModels)
productsAdapter?.notifyDataSetChanged()
})
}
ProductsAdapter
binds the ProductViewModel
with the list_item_products
layout.
As I mentioned in comments in the code, I don't want to create ProductViewModel
my self and instead I want it from dagger only. I also believe the correct approach would be to Inject the ProductsAdapter
directly into the Fragment
, but then also, I need to tell dagger from where it can get Product
object for ProductViewModel
which is available at run time and it ends up on same question for me.
Any guide or directions to achieve this would be really great.