0Pricing
Kotlin Academy · レッスン

KoinによるKMPの依存性注入

マルチプラットフォーム構成でKoinを使い、共有モジュール同士を接続します。

「KoinによるKMPの依存性注入」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

KMPでDIを使う理由

依存性注入は、コンポーネントの生成と利用を分離します。KMPでは、Koinが純粋なKotlinで実装されており、アノテーションプロセッサなしでcommonMainでも動作するため、DIフレームワークの定番です。

// No annotation processing needed — Koin uses lambda DSL
// Add to build.gradle.kts:
// implementation("io.insert-koin:koin-core:3.5.0")
// androidMain: koin-android
// iosMain: no extra dep
fun main() { println("Koin works on all KMP targets") }

KoinモジュールDSL

module { }ブロック内でsingle、factory、scopedを使ってバインディングを定義します。

import org.koin.core.module.dsl.*
import org.koin.dsl.*
val appModule = module {
    single { HttpClient() }               // one instance
    single<UserRepository> { RemoteUserRepository(get()) }
    factory { GetUserUseCase(get()) }      // new instance each time
}

commonMainの共有モジュール

共有KoinモジュールをcommonMainで定義します。プラットフォームモジュールでは、プラットフォーム固有のバインディングを追加します。

// commonMain/di/SharedModule.kt
import org.koin.dsl.module
fun sharedModule() = module {
    single<UserRepository> { RemoteUserRepository(get()) }
    single { GetUserUseCase(get()) }
    single { UserViewModel(get()) }
}

androidMainのプラットフォームモジュール

プラットフォームモジュールは、実際のHTTPエンジン、データベースドライバー、Android固有のシングルトンを提供します。

// androidMain/di/AndroidModule.kt
import io.ktor.client.engine.okhttp.*
import org.koin.dsl.module
fun androidModule() = module {
    single { OkHttp.create() }    // Ktor engine
    single { createHttpClient(get()) }
    includes(sharedModule())
}

Android ApplicationでのstartKoin

AndroidのApplication.onCreateでstartKoin { }を呼び出します。androidContext(this)を使ってコンテキストを利用可能にします。

// androidMain:
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
// In Application.onCreate():
startKoin {
    androidContext(this@MyApp)
    modules(androidModule())
}

iOSでのKoinの開始

iOSでは、共有Koinモジュールを渡して、SwiftのAppDelegateまたはApp構造体からKoinApplication.start()を呼び出します。

// iosMain/KoinInit.kt:
import org.koin.core.context.startKoin
fun initKoin() {
    startKoin {
        modules(iosModule())
    }
}
// Swift: KoinInitKt.doInitKoin() in AppDelegate

依存関係の解決

commonMainのViewModelやユースケースでは、モジュール内でget()を使うか、呼び出し側でinject() / getKoin().get()を使います。

import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class UserViewModel : KoinComponent {
    private val getUser: GetUserUseCase by inject()
    fun load(id: String) = runBlocking { getUser(id) }
}

ViewModelのKoinスコープ

AndroidのViewModelにはAndroidモジュールでviewModel { }を使い、共有VMにはプラットフォームに依存しないfactory { }を使います。

// androidMain (with koin-android):
import org.koin.androidx.viewmodel.dsl.viewModel
val androidVmModule = module {
    viewModel { UserViewModel(get()) }
}
// In Compose:
// val vm: UserViewModel = koinViewModel()

Koinのテスト

テストのセットアップでstartKoinを使い、実際の依存関係をフェイクに置き換えるテストモジュールを読み込みます。

import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.dsl.module
class UserViewModelTest {
    @BeforeTest fun setup() {
        startKoin { modules(module {
            single<UserRepository> { FakeUserRepository() }
            single { GetUserUseCase(get()) }
        })}
    }
    @AfterTest fun teardown() = stopKoin()
}

SingleとFactoryとScopedの比較

single: アプリの存続期間中、1つのインスタンスを使用します。factory: get()を呼び出すたびに新しいインスタンスを作成します。scoped: Koinスコープごとに1つのインスタンスを使用します(画面スコープなど)。

import org.koin.dsl.module
val lifetimeModule = module {
    single { Database() }           // shared, one instance
    factory { Analytics() }         // new on each get()
    scope<ScreenScope> {
        scoped { ScreenViewModel() } // one per screen lifecycle
    }
}

Koinと手動DIとHiltの比較

Koin: マルチプラットフォーム対応で、KSP/KAPTを使わないランタイムDIです。Hilt: Android専用で、コンパイル時にアノテーションで構成するDIです。手動DI: フレームワークのオーバーヘッドがなく、最大限の制御が可能です。KMPプロジェクトでは共有のためにKoinを使用します。

fun main() {
    // Koin  : commonMain, runtime, lambda DSL
    // Hilt  : androidMain only, compile-time
    // Manual: no framework, explicit wiring in factory/module files
    println("Choose Koin for KMP; Hilt for Android-only projects")
}

by inject()による遅延注入

by inject()は、最初にアクセスしたときに依存関係を遅延解決します。KoinComponentを実装するクラスで便利です。

import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class AppService : KoinComponent {
    private val repo: UserRepository by inject()  // resolved on first use
    fun doWork() = println(repo.javaClass.simpleName)
}

確認問題

get()が呼び出されるたびに新しいインスタンスを作成するKoinスコープはどれですか?

まとめ

Koinはアノテーション処理を必要としないラムダDSLを使用するため、KMPに適しています。共有モジュールをcommonMainに、プラットフォームモジュールを各プラットフォームのソースセットに定義します。シングルトンにはsingle、新しいインスタンスにはfactory、遅延解決にはby inject()を使用します。

よくある質問

「KoinによるKMPの依存性注入」レッスンは無料ですか?

はい。「KoinによるKMPの依存性注入」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「KoinによるKMPの依存性注入」で何を学びますか?

マルチプラットフォーム構成でKoinを使い、共有モジュール同士を接続します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「KoinによるKMPの依存性注入」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. KMPプロジェクト構造:commonMain、androidMain、iosMain
  2. プラットフォームAPI向けexpect/actual機構
  3. Repository層とUse Case層の共有
  4. KoinによるKMPの依存性注入
← Kotlin Academyに戻る