Skip to content
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
31 changes: 25 additions & 6 deletions docs/reference/koin-core/scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,23 @@ class FragmentPresenter

## Creating and Using Scopes

### Using Scopes with `use { }`

`Scope` implements `AutoCloseable`, so you can use Kotlin's `use { }` block for safe, automatic cleanup. This is the recommended approach for short-lived scopes (request handling, transactions, batch jobs, etc.):

```kotlin
getKoin().createScope("my_scope_id", named("session")).use { scope ->
val sessionData: SessionData = scope.get()
val prefs: UserPreferences = scope.get()
}
// scope is closed automatically, even on exceptions
```

### Manual Scope Management

For longer-lived scopes, you can manage the lifecycle manually:

```kotlin
// Create a scope
val myScope = getKoin().createScope("my_scope_id", named("session"))

// Get instances from scope
Expand Down Expand Up @@ -324,18 +337,24 @@ fun MyScreen() {

### Closing Scopes

`Scope` implements `AutoCloseable`. The recommended way to handle scope cleanup is with `use { }`:

```kotlin
getKoin().createScope("my_scope", named("session")).use { scope ->
val data: SessionData = scope.get()
}
// All scoped instances released automatically, even on exceptions
```

When a scope closes:
1. All scoped instances are released
2. `onClose` callbacks are invoked
3. Scope becomes unusable

```kotlin
// Manual close is also supported
val scope = getKoin().createScope("my_scope", named("session"))

// Use the scope
val data: SessionData = scope.get()

// Close when done
scope.close() // SessionData instance released

// This throws an exception
Expand Down Expand Up @@ -406,7 +425,7 @@ class CheckoutActivity : AppCompatActivity(), AndroidScopeComponent {

1. **Use singletons sparingly** - Only for truly app-wide dependencies
2. **Scope shared state** - When multiple components need the same instance
3. **Close scopes explicitly** - Don't rely on garbage collection
3. **Close scopes explicitly** - Use `scope.use { }` for short-lived scopes, or call `close()` manually
4. **Keep scopes focused** - Don't put everything in one scope
5. **Use Android scope components** - For automatic lifecycle management

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class Scope(
val scopeArchetype : TypeQualifier? = null,
@PublishedApi
internal val _koin: Koin,
) : Lockable() {
) : Lockable(), AutoCloseable {

internal val linkedScopes = ArrayList<Scope>()
@KoinInternalApi
Expand Down Expand Up @@ -431,7 +431,7 @@ class Scope(
/**
* Close all instances from this scope
*/
fun close() = KoinPlatformTools.synchronized(this) {
override fun close() = KoinPlatformTools.synchronized(this) {
_koin.logger.debug("|- (-) Scope - id:'$id'")

_callbacks.forEach { it.onScopeClose(this) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,46 @@ class ScopeAPITest {
assertTrue(closed)
}

@Test
fun `scope implements AutoCloseable and works with use`() {
val scope = koin.createScope("myScope", scopeKey)
var closed = false
scope.registerCallback(object : ScopeCallback {
override fun onScopeClose(scope: Scope) {
closed = true
}
})

val result = scope.use {
it.get<A>()
}

assertNotNull(result)
assertTrue(closed)
}

@Test
fun `scope use closes on exception`() {
val scope = koin.createScope("myScope", scopeKey)
var closed = false
scope.registerCallback(object : ScopeCallback {
override fun onScopeClose(scope: Scope) {
closed = true
}
})

try {
scope.use {
error("test exception")
}
fail()
} catch (e: IllegalStateException) {
assertEquals("test exception", e.message)
}

assertTrue(closed)
}

class MyScopeComponent(private val _koin: Koin) : KoinScopeComponent {
override fun getKoin(): Koin = _koin
override val scope: Scope = createScope()
Expand Down