Skip to content

Instantly share code, notes, and snippets.

@Zhuinden
Last active June 1, 2020 07:46
Show Gist options
  • Save Zhuinden/a8f3d0e98253baab8ebd7147eb6beaa7 to your computer and use it in GitHub Desktop.
Save Zhuinden/a8f3d0e98253baab8ebd7147eb6beaa7 to your computer and use it in GitHub Desktop.
Dagger + ViewModel, first iteration
@Singleton
@Component
class ApplicationComponent {
fun myViewModelProvider(): Provider<MyViewModel>
}
class MyFragment: Fragment() {
lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val component = (requireActivity().application as MyApplication).component
viewModel = createViewModel { component.myViewModelProvider().get() }
}
}
class MyViewModel @Inject constructor(): ViewModel() {
}
inline fun <reified T: ViewModel> ViewModelStoreOwner.createViewModel(crossinline factory: () -> T): T = T::class.java.let { clazz ->
ViewModelProvider(this, object: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if(modelClass == clazz) {
@Suppress("UNCHECKED_CAST")
return factory() as T
}
throw IllegalArgumentException("Unexpected argument: $modelClass")
}
}).get(clazz)
}
@trevjonez
Copy link

@Zhuinden you should be able to have a single createViewModel if you use ViewModelStoreOwner as the receiver.

put all together you landed on almost exactly the way i've been doing it for almost 2 years. πŸ˜†
https://gist.github.com/trevjonez/0535c396b7b79a8fa85827f79f543c52

@Zhuinden
Copy link
Author

Thanks for the note, I was thinking if there are any downsides but I can't see one. This is a great addition and therefore updated the gist. Thanks!

@lukas1
Copy link

lukas1 commented Jun 1, 2020

this code doesn't compile :-) Where does the clazz property come from? Also this line if(modelClass == T::class.java) { does not compile, because in that context T is not reified. And you cannot use reified in the overriding function. Also it uses deprecated ViewModelProviders

I believe this is the working and up-to-date version:

inline fun <reified T: ViewModel> ViewModelStoreOwner.createViewModel(crossinline factory: () -> T): T {
    val clazz = T::class.java
    return ViewModelProvider(this, object : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            if (modelClass == clazz) {
                @Suppress("UNCHECKED_CAST")
                return factory() as T
            }
            throw IllegalArgumentException("Unexpected argument: $modelClass")
        }
    }).get(clazz)
}

@Zhuinden
Copy link
Author

Zhuinden commented Jun 1, 2020

That's what I get for trying to remove a .let { clazz - > and make the example a bit clearer, but do that only in a Gist πŸ€” I'll just copy the real thing when I'm near my pc

@Zhuinden
Copy link
Author

Zhuinden commented Jun 1, 2020

Thanks for letting me know, I've just replaced it with the real code instead. Now it can't not work.

@lukas1
Copy link

lukas1 commented Jun 1, 2020

I'd also suggest to update the example to not use ViewModelProviders . It's deprecated: https://developer.android.com/reference/androidx/lifecycle/ViewModelProviders

@Zhuinden
Copy link
Author

Zhuinden commented Jun 1, 2020

that's what I get for copy-pasting from Stack Overflow from an answer I should clearly update as it's like a year old πŸ˜…

But yes, you are right. Now I've updated both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment