ContributesGraphExtension
Generates a graph extension when the parent graph interface is merged.
The Problem
Imagine this module dependency tree:
:app
/ \
v v
:login :user-data
:app
defines the main dependency graph with @DependencyGraph
. The :login
module defines a graph extension for authenticated user flows, and :user-data
provides some core functionality like UserRepository
.
If :login
defines its own graph directly with @DependencyGraph
, it won't see contributions from :user-data
unless :login
depends on it directly.
The Solution
Instead, :login
can use @ContributesGraphExtension(LoggedInScope::class)
to say: "I want to contribute a new graph extension to the app graph." The extension will be generated in :app
, which already depends on both :login
and :user-data
. Now UserRepository
can be injected in LoggedInGraph
.
@ContributesGraphExtension(LoggedInScope::class)
interface LoggedInGraph {
val userRepository: UserRepository
@ContributesGraphExtension.Factory(AppScope::class)
interface Factory {
fun createLoggedInGraph(): LoggedInGraph
}
}
In the :app
module:
@DependencyGraph(AppScope::class, isExtendable = true)
interface AppGraph
The generated code will modify AppGraph
to implement LoggedInGraph.Factory
and implement createLoggedInGraph()
using a generated final $$ContributedLoggedInGraph
class that includes all contributed bindings, including UserRepository
from :user-data
.
// modifications generated during compile-time
interface AppGraph : LoggedInGraph.Factory {
override fun createLoggedInGraph(): LoggedInGraph {
return $$ContributedLoggedInGraph(this)
}
// Generated in IR
class LoggedInGraph$$MetroGraph(appGraph: AppGraph) : LoggedInGraph {
// ...
}
}
Finally, you can obtain a LoggedInGraph
instance from AppGraph
since it now implements LoggedInGraph.Factory
:
// Using the asContribution() intrinsic
val loggedInGraph = appGraph.asContribution<LoggedInGraph.Factory>().createLoggedInGraph()
// Or if you have IDE support enabled
val loggedInGraph = appGraph.createLoggedInGraph()
Graph arguments
You can pass arguments to the graph via the factory:
@ContributesGraphExtension.Factory(AppScope::class)
interface Factory {
fun create(@Provides userId: String): LoggedInGraph
}
This maps to:
// Generated in IR
@DependencyGraph(LoggedInScope::class)
class $$ContributedLoggedInGraph(
@Extends parent: AppGraph,
@Provides userId: String
): LoggedInGraph {
// ...
}
In AppGraph
, the generated factory method looks like:
// Generated in IR
override fun create(userId: String): LoggedInGraph {
return LoggedInGraph$$MetroGraph(this, userId)
}
Note: Abstract factory classes cannot be used as graph contributions.
Contributed graphs may also be chained, but note that isExtendable must be true to do so!