Archie G. Quiñones
Archie G. Quiñones

Reputation: 13668

How to scope dagger dependencies with fragments when using Fragment Factory?

Without using FragmentFactory, scoping dependencies within Fragment is straigth forward. Just create a Subcomponent for your fragment and just create the Fragment's Subcomponent in onAttach().

But when using FragmentFactory you no longer inject dependencies via the subcomponent but instead pass then in Fragment's constructor.

I was wondering if I could still declare a dependency which should only last within the fragment's lifecycle using Dagger. I currently could not think of a way to achieve this.

So instead of binding my dependencies to a certain scope, I just declare the dependency with any scope or just use @Reusable on them.

Also, since fragments are created through FragmentFactory, the created Fragments doesn't exist in the DI graph.

How can we properly scope dependencies to fragment and also be able to add the fragment in the DI graph when using FragmentFactory?

Upvotes: 1

Views: 1108

Answers (1)

Nitrodon
Nitrodon

Reputation: 3435

You can accomplish this by making a Subcomponent responsible for creating your Fragment. The Fragment should have the same scope as this Subcomponent.

@Subcomponent(modules = [/* ... */])
@FragmentScope
interface FooSubcomponent {

    fun fooFragment(): FooFragment

    @Subcomponent.Factory
    interface Factory {
        fun create(): FooSubcomponent
    }
}

After taking care of any cyclic dependency issues, this Subcomponent acts the same as if you had explicitly created a subcomponent in onAttach() using @BindsInstance, except that you can (and must) now use constructor injection on FooFragment.

In order to provide FooFragment in your main component (or parent subcomponent), you will need to install this Subcomponent into a module.

@Module(subcomponents = [FooSubcomponent::class])
object MyModule {
    @Provides
    @IntoMap
    @FragmentKey(FooFragment::class)
    fun provideFooFragment(factory: FooSubcomponent.Factory): Fragment {
        return factory.create().fooFragment()
    }
}

Some caveats:

  1. The scenario you describe (FooFragment depends on some class which depends on FooFragment) is the definition of a cyclic dependency. You will need to inject a Lazy or Provider at some point in this cycle, or Dagger will throw an error at compile time.

  2. If you split this up into two steps, first providing FooFragment and then binding it into your map, the subcomponent will see the @Provides method from its parent component. This is likely to cause a StackOverflowError if you aren't careful.

Upvotes: 2

Related Questions