Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
447 changes: 213 additions & 234 deletions docs/di.md

Large diffs are not rendered by default.

23 changes: 13 additions & 10 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ for an incremental adoption. Apps can leverage the concepts and the framework wi
once.

For example, instead of going all in on the unidirectional dataflow, Android apps can start adopting `Presenters` and
`Renderers` on an Activity by Activity or Fragment by Fragment basis. Our Android app initially used
[Dagger 2](https://dagger.dev/) and [Anvil](https://github.com/square/anvil) as dependency injection framework and
made it interop with `kotlin-inject-anvil` before switching fully.
`Renderers` on an Activity by Activity or Fragment by Fragment basis. Today we recommend starting
new App Platform code with Metro. Earlier, our Android app initially used
[Dagger 2](https://dagger.dev/) and [Anvil](https://github.com/square/anvil) as dependency
injection framework and later made it interop with `kotlin-inject-anvil` before switching fully.


#### Can I use [Dagger 2](https://dagger.dev/) or any other DI framework?

It depends, but likely yes. We've chosen [kotlin-inject-anvil](https://github.com/amzn/kotlin-inject-anvil) because
it supports Kotlin Multiplatform and verifies the dependency graph at compile time.
It depends, but likely yes. App Platform recommends [Metro](di.md) as the default DI framework because
it supports Kotlin Multiplatform, verifies the dependency graph at compile time, and is the direction
the framework docs and examples assume.

App Platform provides support for [Metro](di.md) out of the box, but there are still rough edges around the KMP
support. Long term we may consider moving App Platform to Metro alone.
[kotlin-inject-anvil](https://github.com/amzn/kotlin-inject-anvil) remains supported as the
alternative, especially for existing codebases or when you need compatibility with older App Platform
examples.

Dagger 2 is more challenging, because it only supports Android and JVM application. That said, App Platform started on
Android we used to use Dagger 2. We bridged the Dagger 2 components with the `kotlin-inject-anvil` components for
interop and this served us well for a long time until we fully migrated to `kotlin-inject-anvil`.
Dagger 2 is more challenging, because it only supports Android and JVM application. Metro is the
recommended default today, though App Platform started on Android with Dagger 2 and we first
bridged those Dagger 2 components with `kotlin-inject-anvil` for interop.


#### How does App Platform compare to [Circuit](https://slackhq.github.io/circuit/)?
Expand Down
14 changes: 8 additions & 6 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@ usage of the module structure are implemented in the Gradle plugin.

### Dependency Injection

App Platform by default provides support for [kotlin-inject-anvil](di.md#kotlin-inject-anvil) and
[Metro](di.md#metro) as dependency injection solution. But these frameworks aren't enforced and you can
bring your own (1).
App Platform provides first-class support for [Metro](di.md#metro) and
[kotlin-inject-anvil](di.md#kotlin-inject-anvil) as dependency injection solutions. Metro is the
recommended default, but these frameworks aren't enforced and you can bring your own (1).
{ .annotate }

1. In the very first versions of App Platform, we at Amazon used [Dagger 2](https://dagger.dev/) and
[Anvil](https://github.com/square/anvil). Later we migrated to [kotlin-inject-anvil](https://github.com/amzn/kotlin-inject-anvil).
1. Today App Platform recommends [Metro](https://zacsweers.github.io/metro) for new work.
Historically, the very first versions at Amazon used [Dagger 2](https://dagger.dev/) and
[Anvil](https://github.com/square/anvil), and later migrated to
[kotlin-inject-anvil](https://github.com/amzn/kotlin-inject-anvil).

### Scopes

Expand Down Expand Up @@ -91,7 +93,7 @@ and Desktop.

The [Gradle plugin](setup.md) comes with a convenient DSL to take care of many necessary configurations, e.g. it sets
up the *Compose* compiler for *Molecule* and *Compose Multiplatform*. It configures KSP and integrates
*kotlin-inject-anvil* or *Metro* for each platform. It sets the Android namespace and artifact ID when the module
*Metro* or *kotlin-inject-anvil* for each platform. It sets the Android namespace and artifact ID when the module
structure is enabled.

## Getting Started
Expand Down
2 changes: 1 addition & 1 deletion docs/presenter.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class AmazonLoginPresenter : LoginPresenter {

!!! note

`MoleculePresenters` are never singletons. While they use `kotlin-inject-anvil` or Metro for constructor injection and
`MoleculePresenters` are never singletons. While they use Metro or `kotlin-inject-anvil` for constructor injection and
automatically bind the concrete implementation to an API using `@ContributesBinding`, they don't use the
`@SingleIn` annotation. `MoleculePresenters` manage their state in the `@Composable` function with the Compose
runtime. Therefore, it's strongly discouraged to have any class properties.
Expand Down
41 changes: 21 additions & 20 deletions docs/renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,12 @@ different implementations:

### `@ContributesRenderer`

All factory implementations rely on the dependency injection framework `kotlin-inject-anvil` or Metro to discover and
initialize renderers. When the factory is created, it builds the `RendererComponent`, which parent is the app component.
The `RendererComponent` lazily provides all renderers using the multibindings feature. To participate in the lookup,
renderers must tell `kotlin-inject-anvil` or Metro which models they can render. This is done through a component
interface, which automatically gets generated and added to the renderer scope by using the
All factory implementations rely on Metro or `kotlin-inject-anvil` to discover and initialize
renderers. When the factory is created, it builds the generated renderer graph or component, whose
parent is the app graph or component. That generated type lazily provides all renderers using the
multibindings feature. To participate in the lookup, renderers must tell Metro or
`kotlin-inject-anvil` which models they can render. This is done through a generated graph or
component interface, which is automatically added to the renderer scope by using the
[`@ContributesRenderer` annotation](https://github.com/amzn/app-platform/blob/main/kotlin-inject-extensions/contribute/public/src/commonMain/kotlin/software/amazon/app/platform/inject/ContributesRenderer.kt).

Which `Model` type is used for the binding is determined based on the super type. In the following example
Expand All @@ -163,47 +164,47 @@ class LoginRenderer : ComposeRenderer<LoginPresenter.Model>()

??? info "Generated code"

=== "kotlin-inject-anvil"
=== "Metro"

The `@ContributesRenderer` annotation generates following code.

```kotlin
@ContributesTo(RendererScope::class)
interface LoginRendererComponent {
interface LoginRendererGraph {
@Provides
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRenderer(): LoginRenderer = LoginRenderer()

@Provides
@IntoMap
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModel(renderer: () -> LoginRenderer): Pair<KClass<out BaseModel>, () -> Renderer<*>> = LoginPresenter.Model::class to renderer
@RendererKey(LoginPresenter.Model::class)
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModel(renderer: Provider<LoginRenderer>): Renderer<*> = renderer()

@Provides
@IntoMap
@ForScope(scope = RendererScope::class)
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModelKey(): Pair<KClass<out BaseModel>, KClass<out Renderer<*>>> = LoginPresenter.Model::class to LoginRenderer::class
@RendererKey(LoginPresenter.Model::class)
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModelKey(): KClass<out Renderer<*>> = LoginRenderer::class
}
```
=== "Metro"

=== "kotlin-inject-anvil"

The `@ContributesRenderer` annotation generates following code.

```kotlin
@ContributesTo(RendererScope::class)
interface LoginRendererGraph {
interface LoginRendererComponent {
@Provides
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRenderer(): LoginRenderer = LoginRenderer()

@Provides
@IntoMap
@RendererKey(LoginPresenter.Model::class)
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModel(renderer: Provider<LoginRenderer>): Renderer<*> = renderer()

public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModel(renderer: () -> LoginRenderer): Pair<KClass<out BaseModel>, () -> Renderer<*>> = LoginPresenter.Model::class to renderer

@Provides
@IntoMap
@ForScope(scope = RendererScope::class)
@RendererKey(LoginPresenter.Model::class)
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModelKey(): KClass<out Renderer<*>> = LoginRenderer::class
public fun provideSoftwareAmazonAppPlatformSampleLoginLoginRendererLoginPresenterModelKey(): Pair<KClass<out BaseModel>, KClass<out Renderer<*>>> = LoginPresenter.Model::class to LoginRenderer::class
}
```

Expand Down
Loading
Loading