1

I have a Dagger Subcomponent called UserComponent, created when a user signed in to the app, beneath that I have lists of Subcomponents (e.g : FriendsComponent, ProfileComponent, HomeComponent ...).

What I want is this: creating a subComponent called ProfileComponent (under UserComponent) that got two Modules ProfileModule and PostsModule :

                         MainComponent
                             |
                =============|======================
               |                                   |
                |                                   |
          UserComponent                      WelcomeComponent
                |
       =========|============                  
       |                     |    
    ProfileComponent       HomeComponent
       |
       |
  =====================
  |                  |
 PostsModule        ProfileModule   

(I hope this is readable )

so ProfileComponent shall contains this :

@UserScope
@Subcomponent(
       modules = {PostsModule.class, ProfileModule.class}
)
public interface ProfileComponent {

   void inject(PostsFragment postsFragment);
   void inject(ProfileActivity profileActivity);
}

Here is the User SubComponent

@UserScope
@Subcomponent(
       modules = UserModule.class
)
public interface UserComponent {

           HomeComponent plus(HomeModule homeModule);

           ProfileComponent plus(PostsModule postsModule);

           ProfileComponent plus(ProfileModule profileModule);
        }

Injection done here, in PostsFragment

protected void setUpComponent(DragonBloodComponent component) {
       mApp.getApp(getActivity()).getUserComponent()
               .plus(new PostsModule())
               .inject(this);
   }

I get this error

error: Only one method can create a given subcomponent..profile.ProfileComponent is created by: [plus(PostsModule), plus(ProfileModule)]

Am I doing this right? Thank you.

Mohamed ALOUANE
  • 5,349
  • 6
  • 29
  • 60

2 Answers2

6
ProfileComponent plus(PostsModule postsModule);
ProfileComponent plus(ProfileModule profileModule);

These two are incompatible: It looks you want to create a ProfileComponent that requires both modules, but instead you're providing two different ways to create one, and neither has both modules it needs. Dagger happens to warn you about the mutual incompatibility before it warns you that they are both incomplete.

The simplest change, as described in the "Subcomponents" section of the @Component docs, is to put them in the same method declaration (emphasis mine):

Subcomponents may also be declared via a factory method on a parent component or subcomponent. The method may have any name, but must return the subcomponent. The factory method's parameters may be any number of the subcomponent's modules, but must at least include those without visible no-arg constructors.

That would look like this:

ProfileComponent plus(PostsModule postsModule, ProfileModule profileModule);

Builders

As an alternative, define a subcomponent builder (cf. component builder) that can aggregate the modules you need. This subcomponent builder can then be injected anywhere you need it, so you don't need to inject your Component in order to call the plus method. This might also make your code more readable if you need many more modules in your subcomponent.

@UserScope
@Subcomponent(
       modules = {PostsModule.class, ProfileModule.class}
)
public interface ProfileComponent {

   void inject(PostsFragment postsFragment);
   void inject(ProfileActivity profileActivity);

   /**
    * With this you can inject a ProfileComponent.Builder from within
    * the classes that UserComponent provides, or expose it on your enclosing 
    * (sub)component if you'd like. Dagger writes the implementation.
    *
    * Of course, you can skip Modules with zero-arg constructors, because Dagger
    * doesn't need to be provided an instance of those.
    */
   @Subcomponent.Builder interface Builder {
       Builder postsModule(PostsModule postsModule);
       Builder profileModule(ProfileModule profileModule);
       ProfileComponent build();
   }
}

And consumed:

@Inject ProfileComponent.Builder profileComponentBuilder;
ProfileComponent profileComponent = profileComponentBuilder
    .postsModule(yourPostsModule)
    .profileModule(yourProfileModule)
    .build();
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Hey Jeff, Thank you for your amazing well-documented answer.I like the Builder approach and want to implement it, so do I have to add builders for all my component and delete the "plus" methods from my UserComponent ? Thanks. – Mohamed ALOUANE Dec 17 '16 at 11:44
  • @Mohamed You can choose a builder instead of `plus` for one subcomponent at a time—they don't have to all be the same. I haven't tried it with a Builder and "plus method" existing for the same component, but if you like the Builder and like access from the Component you can also have a method return your ProfileComponent.Builder on your UserComponent, giving you Builder syntax with minimal code change. Of course, if you just inject a builder where you need it, deleting it from the Component may be the best choice. – Jeff Bowman Dec 17 '16 at 13:29
  • Okay, and how about UserComponent? right now it look like something https://gist.github.com/alouanemed/9c3c930950613d55b4b48b1bdcecff6d sorry for a lot of questions, am just little confused – Mohamed ALOUANE Dec 17 '16 at 19:37
  • After deleting your incomplete one-argument `plus` methods that return a ProfileComponent, your UserComponent has three possible outcomes: (1) one _two-argument_ `plus` method that returns a `ProfileComponent`, as above; (2) one zero-argument method that returns a `ProfileComponent.Builder`, if you've defined one; or (3) neither 1 nor 2, because you can just `@Inject` your `ProfileComponent.Builder` wherever you need it without having to retrieve or keep the `UserComponent` to get to it. – Jeff Bowman Dec 18 '16 at 21:25
  • Hey Jeff, I've been testing your response and I got stuck at this point: I created UserComponent builder, but how about all those plus methods I've got for each subcomponent ? I mean how dagger will know that InboxComponent is under UserComponent without been mentioning it like I done with the "plus" method https://gist.github.com/alouanemed/9c3c930950613d55b4b48b1bdcecff6d – Mohamed ALOUANE Dec 23 '16 at 20:32
  • Achieved that using something like this : UserComponent.Builder userComponentBuilder(); Thanks Jeff :) !! – Mohamed ALOUANE Dec 24 '16 at 23:05
  • How can I initialize sub component ? I have same question here: http://stackoverflow.com/questions/43214994/dagger-2-inject-after-define-subcomponent – Trần Kim Dự Apr 04 '17 at 19:23
0

I am not the most familiar with subcomponents, but I think the reason you are getting that is because your Profile Component contains PostModules.class as a subcomponent and then you are trying to inject it again in your PostsFragment class.

dazza5000
  • 7,075
  • 9
  • 44
  • 89