Alexandre Gressier
04/17/2025, 1:02 PMAlexandre Gressier
04/17/2025, 1:02 PM:network:http
← :network:graphql
← :data
← :app
With the following components:
// :network:http
@ContributesTo(AppScope::class)
@SingleIn(AppScope::class)
interface NetworkHttpComponent {
@Provides
fun provideHttpClient(): HttpClient = HttpClient()
}
// :network:graphql
@ContributesTo(AppScope::class)
@SingleIn(AppScope::class)
interface CoreNetworkGraphqlComponent {
@Provides
fun graphqlClient(httpClient: HttpClient): ApolloClient =
ApolloClient.Builder()
.serverUrl("<http://example.com/graphql>")
.ktorClient(httpClient)
.build()
}
// :data
interface AuthRepository {
fun getWelcomeMessage(): Flow<String>
}
@Inject
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
class DefaultAuthRepository(val graphqlClient: ApolloClient) : AuthRepository {
override fun getWelcomeMessage(): Flow<String> = {…}
}
// :app
@MergeComponent(AppScope::class)
@SingleIn(AppScope::class)
abstract class AppComponent() {…}
Since :app
uses @MergeComponent
, it only works if every module depend on eachother via api()
instead of implementation()
. So that’s definitely not correct.
Looking at the generated code in :app
, it makes sense to need api()
right now because every single dependency appears in it:
public class InjectKotlinInjectAppComponent(
appDelegate: Application,
) : KotlinInjectAppComponent(appDelegate),
ScopedComponent {
override val _scoped: LazyMap = LazyMap()
override val viewModelFactory: ViewModelProvider.Factory
get() = _scoped.get("@ForScope(scope=AppScope)" + "androidx.lifecycle.ViewModelProvider.Factory") {
provideViewModelFactory(
viewModelFactory = KotlinInjectViewModelFactory(
viewModelMap = mapOf(
provideOnboardingViewModel(
factory = {
OnboardingViewModel(
authRepository = provideDefaultAuthRepositoryAuthRepository(
defaultAuthRepository = _scoped.get("com.example.`data`.DefaultAuthRepository") {
DefaultAuthRepository(
graphqlClient = graphqlClient(
httpClient = provideHttpClient()
)
// ...
I took a look at the sample app, but since there’s only one :lib
module it does not help here. There’s probably something simple that I am missing, but since I don’t know much about Anvil in the first place I’m at loss. Do I need other scopes than AppScope
? Any help will be appreciated!blakelee
04/17/2025, 3:34 PMblakelee
04/17/2025, 3:36 PMblakelee
04/17/2025, 3:38 PMAlexandre Gressier
04/17/2025, 3:39 PMAlexandre Gressier
04/17/2025, 3:42 PMblakelee
04/17/2025, 3:58 PM:feature:feature-name:public
:feature:feature-name:impl
:feature:feature-name:impl-robots
:feature:feature-name:test
:feature:feature-name:demo
:feature:feature-name:fake
public
can be included in other feature modules and is just the exposed api for other modules to use. No anvil here and it’s usually just plain old kotlin objects. public only imports other public
impl
provides the actual implementations for public
impl-robots
test robots for app, demo apps, and whatever other modules want to use functionality
test
testing utils for other modules (not the tests themselves)
demo
an isolated demo app useful for just testing a specific features. I might have just one feature tested through this. Having a demo app means I don’t need to do a whole bunch of other things to get to the screen I want to test. I can just load it up directly in the demo app with whatever conditions I want
fake
fakes that can be used for testing. goes great in demo apps or the main app tests. These are the fake implementations of the public module
There’s a few more we used but for a project that isn’t 4500 modules or whatever Square is at now, a few will suffice.
A lot of this might be unnecessary depending on the setup, but having a public
and impl
module at minimum has saved me a ton of headache.blakelee
04/17/2025, 3:58 PMimpl
modules are all included in my top level app and I just need to include public
everywhere.Alexandre Gressier
04/17/2025, 4:05 PMblakelee
04/17/2025, 4:06 PMralf
04/17/2025, 4:16 PMkotlin-inject-anvil
. The sample in this project is larger and shows all of this in practice.Alexandre Gressier
04/17/2025, 4:22 PMAlexandre Gressier
04/18/2025, 6:55 PM:impl
modules”. Why is the :app
module allowed to have :impl
at all? Is it solely for generating the object graph? Can’t sub object graphs be generated into submodules and then merged? Why is it not needed in Dagger/Hilt in NowInAndroid for example?blakelee
04/18/2025, 7:25 PMAlexandre Gressier
04/18/2025, 7:29 PM:impl
as transitive somehow (which is wrong), and not the fact that yeah they would actually be leftovers otherwise