Anmol
Anmol

Reputation: 8670

how to inject View model using koin? (for specific use case)

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.

Upvotes: 3

Views: 12395

Answers (2)

Birju Vachhani
Birju Vachhani

Reputation: 6373

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)
}

}

Upvotes: 0

Son Huynh
Son Huynh

Reputation: 939

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)

Upvotes: 0

Related Questions