Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created koin-decompose module #2062

Open
wants to merge 5 commits into
base: 4.1.0
Choose a base branch
from
Open
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
66 changes: 66 additions & 0 deletions docs/reference/koin-decompose/decompose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: Managing Decompose Scopes
---

The `koin-decompose` module is dedicated to manage scopes inside the Decompose components. Works pretty the same as [Android setup](/reference/koin-android/scope)

## Scope for Decompose components

### Declare a Decompose Scope

To scope dependencies on an Decompose component, you have to declare a scope section with the `scope` block like follow:

```kotlin
class MyUseCase()
class MyDelegate(val useCase : MyUseCase)

module {
// Declare scope for MyComponent
scope<MyComponent> {
// get MyDelegate instance from current scope
scoped { MyDelegate(get()) }
scoped { MyUseCase() }
}
}
```

### Decompose Scope Interface

Under the hood, Decompose scopes needs to be used with `DecomposeScopeComponent` interface to implement `scope` field like this:

```kotlin
class MyComponent(
context: ComponentContext,
) : ComponentContext by context, DecomposeScopeComponent {

override val scope: Scope by componentScope()
}
```

We need to use the `DecomposeScopeComponent` interface and implement the `scope` property. This will setting up the default scope used by your class.

### Decompose Scope API

To create a Koin scope bound to a Decompose component, just use the following function:
- `createComponentScope()` - Create Scope for current Component (scope is not linked to the parent component)

This function is available as delegate, to implement scope:

- `componentScope()` - Create Scope for current Component (scope is not linked to the parent component)

### DecomposeScopeComponent and Scope closing handling

You can run some code before Koin Scope is being destroyed, by overriding `onCloseScope` function from `DecomposeScopeComponent`:

```kotlin
class MyComponent(
context: ComponentContext,
) : ComponentContext by context, DecomposeScopeComponent {

override val scope: Scope by componentScope()

override fun onCloseScope() {
// Called before closing the Scope
}
}
```
2 changes: 2 additions & 0 deletions projects/bom/koin-bom/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ dependencies {
api(project(":compose:koin-compose-viewmodel-navigation"))
api(project(":compose:koin-androidx-compose"))
api(project(":compose:koin-androidx-compose-navigation"))

api(project(":decompose:koin-decompose"))
}
}

Expand Down
14 changes: 14 additions & 0 deletions projects/decompose/koin-decompose/api/koin-decompose.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
public final class org/koin/decompose/ComponentContextExtKt {
public static final fun componentScope (Lorg/koin/decompose/DecomposeScopeComponent;)Lkotlin/Lazy;
public static final fun createComponentScope (Lorg/koin/decompose/DecomposeScopeComponent;)Lorg/koin/core/scope/Scope;
}

public abstract interface class org/koin/decompose/DecomposeScopeComponent : com/arkivanov/decompose/ComponentContext, org/koin/core/component/KoinScopeComponent {
public abstract fun onScopeClose (Lorg/koin/core/scope/Scope;)V
}

public final class org/koin/decompose/DecomposeScopeComponent$DefaultImpls {
public static fun getKoin (Lorg/koin/decompose/DecomposeScopeComponent;)Lorg/koin/core/Koin;
public static fun onScopeClose (Lorg/koin/decompose/DecomposeScopeComponent;Lorg/koin/core/scope/Scope;)V
}

48 changes: 48 additions & 0 deletions projects/decompose/koin-decompose/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
alias(libs.plugins.kotlinMultiplatform)
}

val koinVersion: String by project
version = koinVersion

kotlin {
jvmToolchain(11)
jvm {
withJava()
}

js(IR) {
nodejs()
browser()
binaries.executable()
}

wasmJs {
nodejs()
binaries.executable()
}

iosX64()
iosArm64()
iosSimulatorArm64()
macosX64()
macosArm64()

sourceSets {
commonMain.dependencies {
api(project(":core:koin-core"))
api(libs.decompose)
}
}
}

tasks.withType<KotlinCompile>().all {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}

apply(from = file("../../gradle/publish.gradle.kts"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.koin.decompose

import com.arkivanov.essenty.lifecycle.doOnDestroy
import org.koin.core.component.getScopeId
import org.koin.core.component.getScopeName
import org.koin.core.scope.Scope
import org.koin.core.scope.ScopeCallback

/**
* Create Scope for Component.
* Doesn't link to parent component's Scope
*/
fun DecomposeScopeComponent.createComponentScope() =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tag it as experimental with @KoinExperimentalApi

getKoin().getScopeOrNull(getScopeId()) ?: createScopeForCurrentLifecycle()

/**
* Provide scope tied to Component
*/
fun DecomposeScopeComponent.componentScope() = lazy { createComponentScope() }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tag it as experimental with @KoinExperimentalApi


private fun DecomposeScopeComponent.createScopeForCurrentLifecycle(): Scope {
val scope = getKoin().createScope(getScopeId(), getScopeName(), this)
scope.registerCallback(
object : ScopeCallback {
override fun onScopeClose(scope: Scope) = this@createScopeForCurrentLifecycle.onScopeClose(scope)
},
)
lifecycle.doOnDestroy {
if (scope.isNotClosed()) {
scope.close()
}
}
return scope
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.koin.decompose

import com.arkivanov.decompose.ComponentContext
import org.koin.core.component.KoinScopeComponent
import org.koin.core.scope.Scope

/**
* Decompose Component that can handle a Koin Scope
*/
interface DecomposeScopeComponent : KoinScopeComponent, ComponentContext {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tag it as experimental with @KoinExperimentalApi


/**
* Called before closing a scope, on onDestroy
*/
fun onScopeClose(scope: Scope) {}
}
6 changes: 6 additions & 0 deletions projects/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ ktor = "2.3.12" #to be removed after Koin 4.1
ktor3 = "3.0.1"
slf4j = "2.0.16"
uuidVersion = "0.8.4"
#decompose
decompose = "3.2.2"

benchmark = "0.4.13"

Expand Down Expand Up @@ -88,6 +90,10 @@ jb-savedstate = { module = "org.jetbrains.androidx.savedstate:savedstate", versi
jb-composeNavigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "composeNavigation" }
jb-navigation = { module = "org.jetbrains.androidx.navigation:navigation-common", version.ref = "composeNavigation" }
uuid = { module = "com.benasher44:uuid", version.ref = "uuidVersion" }

#decompose
decompose = { group = "com.arkivanov.decompose", name = "decompose", version.ref = "decompose" }

# Benchmark
benchmark-runtime = {module = "org.jetbrains.kotlinx:kotlinx-benchmark-runtime", version.ref = "benchmark"}

Expand Down
2 changes: 2 additions & 0 deletions projects/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ include(
":compose:koin-compose-viewmodel-navigation",
":compose:koin-androidx-compose",
":compose:koin-androidx-compose-navigation",
// Decompose
":decompose:koin-decompose",
// Plugin
":plugins:koin-gradle-plugin",
// BOM
Expand Down