Trying to create ViewModel in a dynamic feature module with private val viewModel: PostDetailViewModel by viewModels()
in fragment
class PostDetailFragment : DynamicNavigationFragment<FragmentPostDetailBinding>() {
private val viewModel: PostDetailViewModel by viewModels()
override fun getLayoutRes(): Int = R.layout.fragment_post_detail
override fun bindViews() {
// Get Post from navigation component arguments
val post = arguments?.get("post") as Post
dataBinding.item = post
viewModel.updatePostStatus(post)
}
override fun onCreate(savedInstanceState: Bundle?) {
initCoreDependentInjection()
super.onCreate(savedInstanceState)
}
private fun initCoreDependentInjection() {
val coreModuleDependencies = EntryPointAccessors.fromApplication(
requireActivity().applicationContext,
DomainModuleDependencies::class.java
)
DaggerPostDetailComponent.factory().create(
coreModuleDependencies,
requireActivity().application
)
.inject(this)
}
}
results error
Caused by: java.lang.InstantiationException: java.lang.Class<com.x.post_detail.PostDetailViewModel> has no zero argument constructor
it works in any fragment in app module but not working in dynamic feature modules. What's the proper way to add ViewModels to dynamic feature modules? Should i create ViewModels in app module with a ViewModelFactory and get them from app module?
Based on this official github posts
There's documentation on Hilt and DFM now at https://developer.android.com/training/dependency-injection/hilt-multi-module#dfm
In general though, because we're built off of subcomponents and monolithic components you won't be able to use the standard Hilt mechanisms like @AndroidEntryPoint with DFM.
Unfortunately, no. @ViewModelInject uses the Hilt ActivityRetainedComponent which is monolithic, so any @ViewModelInject class in your DFM won't be recognized.
it seems that injecting to a ViewModel only with @ViewModelInject
and by viewModels()
in a dynamic feature module is not possible as of now.
Based on plaid app i rebuilt my Dagger module in dynamic feature module as
@InstallIn(FragmentComponent::class)
@Module
class PostDetailModule {
@Provides
fun providePostDetailViewModel(fragment: Fragment, factory: PostDetailViewModelFactory) =
ViewModelProvider(fragment, factory).get(PostDetailViewModel::class.java)
@Provides
fun provideCoroutineScope() = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
}
And ViewModel and ViewModelFactory are
class PostDetailViewModel @ViewModelInject constructor(
private val coroutineScope: CoroutineScope,
private val getPostsUseCase: UseCase
) : ViewModel() {
// Do other things
}
class PostDetailViewModelFactory @Inject constructor(
private val coroutineScope: CoroutineScope,
private val getPostsUseCase: UseCase
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass != PostDetailViewModel::class.java) {
throw IllegalArgumentException("Unknown ViewModel class")
}
return PostDetailViewModel(
coroutineScope,
getPostsUseCase
) as T
}
}
And injected to fragment in dynamic feature module
class PostDetailFragment : Fragment() {
@Inject
lateinit var viewModel: PostDetailViewModel
override fun onCreate(savedInstanceState: Bundle?) {
initCoreDependentInjection()
super.onCreate(savedInstanceState)
}
private fun initCoreDependentInjection() {
val coreModuleDependencies = EntryPointAccessors.fromApplication(
requireActivity().applicationContext,
DomainModuleDependencies::class.java
)
DaggerPostDetailComponent.factory().create(
dependentModule = coreModuleDependencies,
fragment = this
)
.inject(this)
}
}