Skip to content

Work with Dagger #69

@chrisbanes

Description

@chrisbanes
Contributor

At the moment I see no way of integrating with an existing ViewModelProvider.Factory. This is necessary to be able to use Dagger to inject ViewModels.

Example fragment

Is there a way?

Activity

chrisbanes

chrisbanes commented on Aug 30, 2018

@chrisbanes
ContributorAuthor

I settled on the following for now commit

I inject the ViewModel.Factory into the Activity, then that to create the VM. Works nicely but we do lose the initial state nice-ness.

gpeal

gpeal commented on Aug 30, 2018

@gpeal
Collaborator

@chrisbanes The reason we moved away from that is because initialState is pretty critical to MvRx. This would prevent a ViewModel from being able to use Fragment args or for MvRx to do proper ViewModel restoration in a new process.

Thoughts?

chrisbanes

chrisbanes commented on Aug 31, 2018

@chrisbanes
ContributorAuthor

Yeah, we don't have assisted DI with Dagger so it's hard to get the initialState + fragment args working nicely.

I managed to get it sort of working, but I can't rely on init {} and have to manually poke the VM to use the fragment args: here.

I might have a play with AutoFactory and see if I can get it to work.

changed the title [-]Integrate with existing ViewModelProviders and ViewModelProvider.Factory[/-] [+]Work with Dagger[/+] on Aug 31, 2018
chrisbanes

chrisbanes commented on Aug 31, 2018

@chrisbanes
ContributorAuthor

Renaming this to be clearer what the goal is (for me anyway).

I got AutoFactory working here. The only downside I see is that MvRxViewModelFactory only passes along the Activity, whereas I would much rather inject the related fragment instead.

Can we get a version of MvRxViewModelFactory which provides the Fragment?

mzgreen

mzgreen commented on Aug 31, 2018

@mzgreen

@chrisbanes have you seen this: https://github.com/square/AssistedInject ? Maybe it'll be helpful in this scenario?

chrisbanes

chrisbanes commented on Aug 31, 2018

@chrisbanes
ContributorAuthor

@mzgreen Ah yes! I've been wanting a reason to try it but totally forgot about it. I'll try on Monday.

chrisbanes

chrisbanes commented on Sep 3, 2018

@chrisbanes
ContributorAuthor
gpeal

gpeal commented on Oct 4, 2018

@gpeal
Collaborator

@chrisbanes I implemented AssistedInject at Tonal and it's working great! Thank you for putting in that work. I'll try and make a wiki page for it.

mtilbrook-dev

mtilbrook-dev commented on Nov 15, 2018

@mtilbrook-dev
Contributor

@chrisbanes I was not able to find a way to create a Dagger ViewModelProvider.Factory, But, I did find a nice-ish way to inject a ViewModelFactory Provider map via the activity. Here is all the boilerplate
https://gist.github.com/marukami/3024dd7ae399a4a33df3041a9af84401

Here is the ViewModel example; I can't get my head around how I would remove the downcast and keep dagger happy.

class MyViewModel @AssistedInject constructor(
  @Assisted state: MvRxState,
  private val api: Api
) : MvRxViewModel<MyViewModelState>(state as MyViewModelState) {

  @AssistedInject.Factory
  interface Factory : ViewModelFactory {
    override fun create(state: MvRxState): MyViewModel
  }

  companion object : MvRxViewModelFactory<MyViewModelState> {
    @JvmStatic override fun create(
      activity: FragmentActivity,
      state: MyViewModelState
    ): BaseMvRxViewModel<MyViewModelState> =
      activity.daggerCreate(
        factory = MyViewModel.Factory::class.java,
        state = state
      )
  }
}
chrisbanes

chrisbanes commented on Nov 15, 2018

@chrisbanes
ContributorAuthor
TheLester

TheLester commented on Nov 16, 2018

@TheLester

It would be nice to have MvRxViewModelFactory with provided fragment, as @chrisbanes mentioned. Otherwise, activity should hold all the viewmodel's factories.

pwillmann

pwillmann commented on Dec 6, 2018

@pwillmann

@chrisbanes Thanks for wrapping your head around all of this, using activities to store the factories works well!

Do you have any idea how this might work in a single activity project with multi gradle modules where each feature module only has fragments and therefore no access/ knowledge of the activity? -> ((activity as ShowDetailsActivity).episodeDetailsViewModelFactory.create(state) in the view models companion object does not work anymore.

mtilbrook-dev

mtilbrook-dev commented on Dec 7, 2018

@mtilbrook-dev
Contributor

I have been using a fork I made that adds a fragment factory #148.

With the fork, you can use the AssistedInject to inject the fragment and then call the factory in the Fragment from the factory like how @chrisbanes was doing with an Activity ViewModel.

class MyFragment :
  BaseMvRxFragment() {
  @Inject lateinit var viewModelFactory: MyViewModel.Factory
}

class MyViewModel @AssistedInject constructor(
  @Assisted state: MyState,
  private val api: TestApi
) : MvRxViewModel<MyState>(state) {

  @AssistedInject.Factory
  interface Factory : ViewModelFactory<MyState> {
    override fun create(state: MyState): MyViewModel
  }

  companion object : MvRxFragmentViewModelFactory<MyState> {
    @JvmStatic override fun create(
      fragment: Fragment,
      state: MyState
    ): BaseMvRxViewModel<MyState> =
      (fragment as MyFragment).viewModelFactory.create(state)
  }

}

Any feedback on doing it this way would be awesome.

added a commit that references this issue on Dec 8, 2018
pwillmann

pwillmann commented on Dec 13, 2018

@pwillmann

Looks good to me, this would really help using MvRx in dagger projects with multiple gradle modules, where not every module has its own activity.

Can we merge this @gpeal ?

added a commit that references this issue on Dec 18, 2018
gpeal

gpeal commented on Apr 17, 2019

@gpeal
Collaborator

@pwillmann We weren't able to merge that change, unfortunately. You can follow that discussion here.

gpeal

gpeal commented on Apr 17, 2019

@gpeal
Collaborator

However, the tivi app from @chrisbanes was able to use AssistedInject and in our app, we use a MvRxViewModelFactory and manually get the dependencies from the component to do constructor injection. I'm going to close this issue for now since I'm not sure if there is anything actionable at this point.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @chrisbanes@gpeal@mtilbrook-dev@pwillmann@TheLester

        Issue actions

          Work with Dagger · Issue #69 · airbnb/mavericks