3

I am having a shared View Model for activity and it's fragment.

My view model need's argument to be passed when instantiating from the activity(onCreate only once)

viewModel =ViewModelProviders.of(this,
                                 NoteViewModelFactory(application!!,
                                                      uid = intent!!.getStringExtra("uid")!!))
                             .get(NoteViewModel::class.java)

But from fragment i don't need to pass the argument as i am sure the i have the argument's passed once.

viewModel = ViewModelProviders.of(activity!!).get(NoteViewModel::class.java)

In Koin i tried doing below.

val noteModule = module(override = true) {
    viewModel { (id: String) -> NoteViewModel(androidApplication(), id) }
}

in Activity:

private val viewModel: NoteViewModel by viewModel { parametersOf(intent!!.getStringExtra("uid")!!) }

in Fragment:

private val viewModel: NoteViewModel by sharedViewModel()

Application Crashed with below error:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.andor.navigate.notepad/com.andor.navigate.notepad.listing.NotesActivity}: org.koin.core.error.InstanceCreationException: Could not create instance for [type:Factory,primary_type:'com.andor.navigate.notepad.core.NoteViewModel'] at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [type:Factory,primary_type:'com.andor.navigate.notepad.core.NoteViewModel'] at org.koin.core.instance.DefinitionInstance.create(DefinitionInstance.kt:61) at org.koin.core.instance.FactoryDefinitionInstance.get(FactoryDefinitionInstance.kt:37) at org.koin.core.definition.BeanDefinition.resolveInstance(BeanDefinition.kt:70) at org.koin.core.scope.Scope.resolveInstance(Scope.kt:165)

I am not able to understand how to solve this using KOIN.

P.S:i am new to koin DI.

Anmol
  • 8,110
  • 9
  • 38
  • 63

2 Answers2

0

Is there anything wrong when you init koin in application class? I tried your code without any issuses. I'm using koin version 2.0.1

class App : Application() {

    override fun onCreate() {
        super.onCreate()

        val noteModule = module(override = true) {
            viewModel { (id: String) -> NoteViewModel(androidApplication(), id) }
        }

        startKoin {
            androidContext(this@App)
            modules(
                noteModule
            )
        }
    }

}

Activity and fragment:

class MainActivity : AppCompatActivity() {

    private val viewModel: NoteViewModel by viewModel { parametersOf(intent!!.getStringExtra("uid")) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.d("NoteViewModel", "id: ${viewModel.id}")

        supportFragmentManager.beginTransaction().replace(R.id.main_root, Frag()).commit()
    }

}
class Frag : Fragment() {

    private val viewModel: NoteViewModel by sharedViewModel()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        Log.d("NoteViewModel", "id: ${viewModel.id}")
        return inflater.inflate(R.layout.activity_main, container, false)
    }
}

View model class:

class NoteViewModel (application: Application, val id: String) : AndroidViewModel(application)
Son Huynh
  • 839
  • 1
  • 7
  • 11
  • Can you please read my question again. I know how to inject viewModel but i have a specific case which i want to solve. – Anmol Jun 26 '19 at 06:24
  • I have tried above and getting the crash as i have posted in the question – Anmol Jun 26 '19 at 12:37
0

You shouldn't pass these type of arguments in ViewModel constructor. Instead what you can do is on your Activity's onCreate(), you set that passed value to the ViewModel. So when you will access that ViewModel in your fragment, you'll surely have that value already set.

class NoteViewModel (application: Application) : AndroidViewModel(application)
{
    var id:String = ""   
}

Your koin Module:

val noteModule = module(override = true) {
    viewModel { NoteViewModel(androidApplication()) }
}

Activity:

class MainActivity : AppCompatActivity() {

    private val viewModel: NoteViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel.id = intent?.getStringExtra("uid")?: ""

        supportFragmentManager.beginTransaction().replace(R.id.container, MyFrag()).commit()
    }
}

Fragment:

class MyFrag : Fragment() {

private val viewModel: NoteViewModel by sharedViewModel()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    // your value will be available here.
    return inflater.inflate(R.layout.activity_main, container, false)
}

}

Birju Vachhani
  • 6,072
  • 4
  • 21
  • 43
  • 1
    `You shouldn't pass these type of arguments in ViewModel constructor.` but why? ViewModelFactory is specifically for these kind of cases. – Anmol Jul 04 '19 at 11:27
  • Well, your code is working for me. I don't know why it doesn't work for you. So, I suggested you one alternative. – Birju Vachhani Jul 05 '19 at 09:45
  • Check this: https://stackoverflow.com/a/62455022/7529648 Also check: https://doc.insert-koin.io/#/koin-android/viewmodel – Isaias Carrera Jun 18 '20 at 16:41