Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot resolve viewModel injection with delegate style declaration #56

Closed
silviorp opened this issue Feb 16, 2018 · 27 comments
Closed

Cannot resolve viewModel injection with delegate style declaration #56

silviorp opened this issue Feb 16, 2018 · 27 comments
Milestone

Comments

@silviorp
Copy link

I'm trying to follow the documentation using delegate style declaration of ViewModels but I cannot resolve the import in Android Studio.
screen shot 2018-02-16 at 18 05 14

This is my code:
screen shot 2018-02-16 at 18 07 08

What I'm doing wrong? Any advice is appreciated.

@fredy-mederos
Copy link
Contributor

Your code looks good to me.
Are you shure you are compiling koin-android-architecture 0.8.2 ??

// Koin for Android Architecture Components
compile "org.koin:koin-android-architecture:0.8.2"

@caleb-allen
Copy link

Are you importing correctly?

import org.koin.android.architecture.ext.viewModel

@caleb-allen
Copy link

Maybe try declaring the type in the value?

val myViewModel : MyViewModel by viewModel()

@silviorp
Copy link
Author

The problem was the version I was using, 0.8.0. But now that I was able to import, there's another problem:

screen shot 2018-02-16 at 21 56 32

Do you guys know how should I proceed? I really like to get the viewModel instance inside my BaseActivity to handle snackbar, toast and other generic observers from my ViewModel.

@caleb-allen
Copy link

You don't need the angle brackets for the viewmodel I believe

@caleb-allen
Copy link

Scratch that I misunderstood that your activity was generic

@caleb-allen
Copy link

What about

val myViewModel : MyViewModel<T> by viewModel()

@arnaudgiuliani
Copy link
Member

Hello,

we can't use generic type T with infered Kotlin parameter :/

Best thing is to add a method to allow such declaration: val myViewModel : MyViewModel<T> by viewModel(T::class) or something like that.

@silviorp
Copy link
Author

silviorp commented Feb 17, 2018

What I trying to do is receive the type of View Model as parameter and inject a new instance of this type. There's no MyViewModel. The ViewModel types would be received in a generic way. The problem is because it's an inline function with reified type, it's not allowed to receive the type like we have here. It would be amazing to be able to use like this: val myViewModel : T by viewModel(T::class) or val myViewModel: T by viewModel()

I'm going to try to strip the injection source code and implement a ViewModelFactory to initialize it inside the BaseActivity but this would not benefit from the rest of the library features

@silviorp
Copy link
Author

After 2 days I had no success trying to make this work.
I'm having a hard time working with generics in kotlin, something I could do in Java very easily.
I don't want to try another libraries for dependency injection but since I'm in the beginning of a new project in the company I work for, if I can't solve this quickly I'll be forced to try another libs or maybe, write this in Java.

@caleb-allen
Copy link

caleb-allen commented Feb 18, 2018

Does

 val myViewModel : T by viewModel()

Not work?

@arnaudgiuliani
Copy link
Member

Yes Ok. Sorry there was a mistake in my answer.
You need to write:

val model: T by viewModel(T::class) or val model: T by lazy {getViewModel(T::class)}

Let me write a patch 👍

@arnaudgiuliani
Copy link
Member

fix is available in current alpha: 0.9.0-alpha-11

@arnaudgiuliani
Copy link
Member

API has been updated to have val model: T by viewModel(<KClass<T>>) and avoid reified types this way.

Your base activity must be like:

class Base Activity<T : ViewModel>(clazz : KClass<T>){
    val model : T by viewModel(clazz)
}

We can't pass reified parameters from class.

@silviorp
Copy link
Author

It worked! Thanks very much!!! I'll try make an extension or something like that to avoid having to pass the class as a parameter but for now, that's working wonderfully!!

@arnaudgiuliani
Copy link
Member

Cool :) I close the issue.

@jakoss
Copy link
Contributor

jakoss commented Feb 26, 2018

@shrpereira Did you manage to avoid having to pass the class as a parameter?

@arnaudgiuliani
Copy link
Member

@shrpereira You will be able to get viewModel by class with viewModelByClass() and getViewModelByClass. This is the final version of the API fir release 0.9.0

@fathallah92
Copy link

fathallah92 commented Nov 18, 2018

@shrpereira Have you added an extension to not pass class as a parameter to the activity ?

@silviorp
Copy link
Author

@fathallah92
No. I've used the following approach:

  • I have a BaseActivity:
open class BaseActivity<out ViewModelType : BaseViewModel>(clazz: KClass<ViewModelType>) :
        AppCompatActivity() {

    val viewModel: ViewModelType by viewModel(clazz)
}
  • And on each activity, I pass the viewModel class like this:
class MainActivity : BaseActivity<MainViewModel>(MainViewModel::class) {}

But I'm not coding on this project for some time (it's in production, though), so, probably there are better ways to do it.

@loalexzzzz
Copy link

@arnaudgiuliani
Are there any elegant approach that not pass class as a parameter to the activity?

@ductranit
Copy link

ductranit commented May 21, 2019

@arnaudgiuliani
Are there any elegant approach that not pass class as a parameter to the activity?

Hope this isn't too late. You can try this:


open class BaseFragment<M : ViewModel> : Fragment() {
    val viewModel: M by lazy { getViewModel(viewModelClass()) }

    @Suppress("UNCHECKED_CAST")
    private fun viewModelClass(): KClass<M> {
        // dirty hack to get generic type https://stackoverflow.com/a/1901275/719212
        return ((javaClass.genericSuperclass as ParameterizedType)
            .actualTypeArguments[0] as Class<M>).kotlin
    }
}

@nEdAy
Copy link

nEdAy commented Nov 6, 2019

@arnaudgiuliani
Are there any elegant approach that not pass class as a parameter to the activity?

Hope this isn't too late. You can try this:


open class BaseFragment<M : ViewModel> : Fragment() {
    val viewModel: M by lazy { getViewModel(viewModelClass()) }

    @Suppress("UNCHECKED_CAST")
    private fun viewModelClass(): KClass<M> {
        // dirty hack to get generic type https://stackoverflow.com/a/1901275/719212
        return ((javaClass.genericSuperclass as ParameterizedType)
            .actualTypeArguments[0] as Class<M>).kotlin
    }
}

There is a problem when using databinding at the same time.

@rasoulmiri
Copy link

@nEdAy if use ViewDataBinding use need change 0 to 1
because ViewModel is 2 in paramaeters

open class BaseFragment<B : ViewDataBinding, M : ViewModel> : Fragment() {
    val viewModel: M by lazy { getViewModel(viewModelClass()) }

    @Suppress("UNCHECKED_CAST")
    private fun viewModelClass(): KClass<M> {
        // dirty hack to get generic type https://stackoverflow.com/a/1901275/719212
        return ((javaClass.genericSuperclass as ParameterizedType)
            .actualTypeArguments[1] as Class<M>).kotlin
    }
}

@Adamshick012
Copy link

Adamshick012 commented Apr 16, 2020

Is there an example of how this is implemented on a example Fragment? I am getting a No definition found error when I use the above example. The only way I have gotten it to work was going around the lazy and doing a loadKoinModules(module{single{ExampleViewModel()}}) inside the ExampleFragment which kind of defeats the purpose.

@loalexzzzz
Copy link

@arnaudgiuliani
Are there any elegant approach that not pass class as a parameter to the activity?

Hope this isn't too late. You can try this:


open class BaseFragment<M : ViewModel> : Fragment() {
    val viewModel: M by lazy { getViewModel(viewModelClass()) }

    @Suppress("UNCHECKED_CAST")
    private fun viewModelClass(): KClass<M> {
        // dirty hack to get generic type https://stackoverflow.com/a/1901275/719212
        return ((javaClass.genericSuperclass as ParameterizedType)
            .actualTypeArguments[0] as Class<M>).kotlin
    }
}

I have figured out by myself, quite similar with your approach , thank you for answer, happy coding :D

@tomek27
Copy link

tomek27 commented Jun 9, 2020

I did it like this

abstract class BaseFragment<VM: BaseViewModel> : Fragment() {

    val viewModel: VM by lazy { getViewModel(clazz) }

    abstract var clazz: KClass<VM>

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests