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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- KMPプロジェクト構造:commonMain、androidMain、iosMain
- プラットフォームAPI向けexpect/actual機構
- Repository層とUse Case層の共有
- KoinによるKMPの依存性注入