12

I need to pass value when the view model is created (userData), so I need to create a view model factory

here is my viewModel, I need application and userData to init this ScoreViewModel

class ScoreViewModel(application: Application, userData: UserKM) : AndroidViewModel(application) {



}

but now I am confused how to pass application when I create viewModel factory

class ScoreViewModelFactory(private val userData: UserKM) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {

            return ScoreViewModel(userData = userData,application = ?????? ) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }

}

what should I do ?

sarah
  • 3,819
  • 4
  • 38
  • 80

1 Answers1

14

You can have something like this :

class Factory(val app: Application) : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(NewsViewModel::class.java)) {
                @Suppress("UNCHECKED_CAST")
                return NewsViewModel(app) as T
            }
            throw IllegalArgumentException("Unable to construct viewmodel")
        }
    }

In your activity or fragment you have :

/**
     * One way to delay creation of the viewModel until an appropriate lifecycle method is to use
     * lazy. This requires that viewModel not be referenced before onActivityCreated, which we
     * do in this Fragment.
     */
    private val viewModel: NewsViewModel by lazy {
        val activity = requireNotNull(this.activity) {
            "You can only access the viewModel after onActivityCreated()"
        }
        ViewModelProviders.of(this, NewsViewModel.Factory(activity.application))
            .get(NewsViewModel::class.java)
    }

And here your viewModel can look like :

class NewsViewModel(application: Application) : AndroidViewModel(application)

For more detail you can look at : https://github.com/Ali-Rezaei/News-Cache/blob/master/app/src/main/java/com/sample/android/news/viewmodels/NewsViewModel.kt

Ali
  • 9,800
  • 19
  • 72
  • 152
  • 2
    so I pass activity application to viewModel factory and to viewModel ? is that permissible ? I mean, as far as I know, we don't make a reference from activities/fragment to viewModel – sarah Mar 21 '20 at 09:42
  • Yes that is completely fine to pass application from your Activity and Fragment. As you see I have a lazy initialization for ViewModel in a Fragment : https://github.com/Ali-Rezaei/News-Cache/blob/master/app/src/main/java/com/sample/android/news/ui/NewsFragment.kt – Ali Mar 21 '20 at 09:44
  • 1
    i have accepted, but I have one more question, if I don't use lazy initialization, will it be a problem ? I initialize the viewModel on onActivityCreated usually – sarah Mar 21 '20 at 10:45
  • 1
    That is also fine, you do not have to lazy initialize. – Ali Mar 21 '20 at 10:51
  • Thanks Ali. I also had a tough inner argument as to whether its a good idea to pass application to the view model – Gilbert Mar 28 '21 at 15:52
  • Do you know how to write test case for this fragment which we have intialize the viewmodel. – Daya Nithi Jul 01 '22 at 03:58
  • @DayaNithi I know how to write test for ViewModel, Repository and UseCase layers but not fragment. Please let us know if you found a solution. – Ali Jul 03 '22 at 09:21