question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Hilt] Question: How to create a subcomponent of a custom component that has a Builder

See original GitHub issue

We’re using Hilt in version 2.34.1-beta.

We have 2 components:

@LoggedUserScope
@DefineComponent(parent = SingletonComponent::class)
interface UserComponent {

    @DefineComponent.Builder
    interface Builder {
        fun setUser(@BindsInstance user: User): Builder
        fun setToken(@BindsInstance string: String): Builder
        fun build(): UserComponent
    }
}

@ProjectScope
@DefineComponent(parent = UserComponent::class)
interface ProjectComponent {

    @DefineComponent.Builder
    interface Builder {
        fun setProject(@BindsInstance project: Project): Builder
        fun build(): ProjectComponent 
    }
}

The UserComponent, without ProjectComponent works fine. We are able to get the instance using EntryPoint. Also, when the ProjectComponent has a parent set to SingletonComponent, everything is OK. But we’d like to have them nested. A logged user can choose a project, but after logging out projects are not available.

With the above code the compiler screams that we need @Provide-annotated method for the ProjectComponent.Builder. As I understand it correctly, the ProjectComponent needs to know the instance of UserComponent to be initialized. How can it be provided? All samples we found are using a parent component without any parameters, so the compiler can instantiate it automagically.

CC: @roshanrajaratnam

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
Chang-Ericcommented, Apr 20, 2021

There is one last thing I noticed that, ProjectDataRepository did not needed to be scoped with @ProjectScope (but still scoped it and seems like it had no effect of not being scoped?).

A scope annotation just tells you whether or not the object should be cached. If you don’t scope it, every time you ask for a ProjectDataRepository you’ll get a new instance. This doesn’t affect the parent/child relationship of UserComponent and ProjectComponent. (Note that it is really the components that have the relationship. I think it gets confusing because colloquially people will often use the scope and component names interchangeably to mean the same thing, but most of the time it is technically the components you should be referring to as the scope annotation really just controls caching within that component).

Anyway, closing this now since it seems everything has been solved.

1reaction
Chang-Ericcommented, Apr 19, 2021

because it’s also the parent of the ProjectComponent

Not sure I followed that, but the Builder isn’t the parent of the ProjectComponent? Or do you mean that because UserComponent is the parent, the child ProjectComponent should be able to be injected the same as the parent?

In any case, you would need something like the following:

// This doesn't really have to be scoped, but it probably is best for it to be, especially if you
// presumably will be storing the ProjectComponent instances in this class.
@LoggedUserScope
public final class UserFoo {
  @Inject UserFoo(ProjectComponent.Builder builder) { ... }
}

// Somewhere where you have your UserManager
UserFoo userFoo = userManager.userComponent().getUserFoo();
userFoo.buildProjectComponent();

The requirement is that the code that injects the ProjectComponent.Builder has to be injected from the parent UserComponent. If it is injected from somewhere else, like the Hilt ViewModelComponent, then it won’t work. The reason injecting the UserComponent.Builder works is because the parent is the Hilt SingletonComponent, so the parent is accessible from the ViewModelComponent since ViewModelComponent is a child of SingletonComponent. ViewModelComponent is not a child of UserComponent however so that is why it does not work for ProjectComponent.Builder.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Creating subcomponents with Dagger Hilt - Stack Overflow
My question is how to implement Provider. You shouldn't have to implement Provider yourself -- Dagger knows how to create a provider of...
Read more >
Custom Components - Hilt - Dagger
To create a custom Hilt component, create a class annotated with @DefineComponent . This will be the class used in @InstallIn annotations. The...
Read more >
Hilt to the rescue, part 2 - FunkyMuse
In this blog post we'll explore how to have our custom component live inside the Hilt hierarchy. Custom components information. The ...
Read more >
Hilt — Adding components to the hierarchy | by Manuel Vivo
Use @DefineComponent to define a new custom component you can use in your app. It takes in the parent component as a parameter....
Read more >
Dagger by Tutorials, Chapter 18: Hilt & Architecture Components
In this chapter, you'll learn more about Hilt, including how to: Use Hilt with other supported Android standard components, like Service s. Create...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found