mcpField
K
kmp-di

kmp-di

github.com/ahmed3elshaer開発6657 回インストール19 週間前に更新
リポジトリ

kmp-di について

kmp-diは、Kotlin MultiplatformプロジェクトでKoinを使った依存性注入を設定するスキルです。共通・プラットフォーム別モジュールや注入パターン、テストを扱い、手動のKotlin DIも代替案として紹介します。

主な機能

  1. 01共通Koinモジュールの設定
  2. 02プラットフォーム固有の依存関係定義
  3. 03ViewModelとScreenModelの注入
  4. 04factory、single、scopedの使い分け
  5. 05DIテストの設定
  6. 06手動Kotlin DIの代替案

活用シーン

  1. 01KMPターゲット間で依存関係定義を共有
  2. 02AndroidとiOSのサービスを設定
  3. 03Compose画面に依存関係を注入
  4. 04テスト用のモックやフェイクを提供

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ahmed3elshaer/everything-claude-code-mobile --skill kmp-di

インストール後、Claude Code で/kmp-diを入力して読み込まれたか確認します。

詳細

KMP の依存性注入

Koin を使用したクロスプラットフォームの依存性注入です。

Koin のマルチプラットフォーム設定

依存関係
// build.gradle.kts (shared module)
sourceSets {
    val commonMain by getting {
        dependencies {
            implementation("io.insert-koin:koin-core:${koinVersion}")
            implementation("io.insert-koin:koin-test:${koinVersion}")
        }
    }
    val androidMain by getting {
        dependencies {
            implementation("io.insert-koin:koin-android:${koinVersion}")
        }
    }
}
モジュール定義
// commonMain/kotlin/di/AppModule.kt
val sharedModule = module {
    // ViewModels (Android) / ScreenModels (multiplatform)
    factory { HomeViewModel(get(), get()) }
    factory { DetailViewModel(get()) }

    // Use Cases
    factory { GetUsersUseCase(get()) }
    factory { GetUserDetailUseCase(get()) }

    // Repositories
    single<UserRepository> { UserRepositoryImpl(get(), get()) }

    // Data Sources
    single { UserApi(get()) }
    single { createDatabase(get()) }
}
プラットフォームモジュール
// androidMain/kotlin/di/PlatformModule.kt
val androidPlatformModule = module {
    includes(sharedModule)

    // Android-specific dependencies
    single { android.content.Context() }
    single { PlatformConnectivityMonitor(get()) }
    single { PlatformFileService(get()) }
}

// iosMain/kotlin/di/PlatformModule.kt
val iosPlatformModule = module {
    includes(sharedModule)

    // iOS-specific dependencies
    single { PlatformConnectivityMonitor() }
    single { PlatformFileService() }
}

Koin の起動

Android
// androidMain/kotlin/MyApp.kt
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApp)
            modules(androidPlatformModule)
        }
    }
}
iOS
// iosMain/kotlin/di/KoinInit.kt
fun initKoin() {
    startKoin {
        modules(iosPlatformModule)
    }
}

// Called from Swift
// KotlinKMMSharedKt.doInitKoin()
Compose Multiplatform
// commonMain/kotlin/Main.kt
fun main() {
    startKoin {
        modules(sharedModule)
    }

    App()
}

ViewModel の注入

Android(ViewModel)
// androidMain/kotlin/ui/HomeScreen.kt
@Composable
fun HomeScreen(
    viewModel: HomeViewModel = koinViewModel()
) {
    val state by viewModel.state.collectAsStateWithLifecycle()
    HomeContent(state)
}
iOS(ScreenModel)
// commonMain/kotlin/ui/HomeScreen.kt
@Composable
fun HomeScreen() {
    val model = getScreenModel<HomeScreenModel>()
    val state by model.state.collectAsState()

    HomeContent(state)
}

リポジトリパターン

Factory と Single
// ✅ factory - creates new instance each time
factory { HomeViewModel(get(), get()) }

// ✅ single - shared instance
single<UserRepository> { UserRepositoryImpl(get(), get()) }

// ✅ scoped - tied to component lifetime
scoped(HomeScope.homeScope) { HomeData(get()) }

名前付き依存関係

// ✅ Named dependencies
module {
    single(named("default")) { DefaultLogger() }
    single(named("analytics")) { AnalyticsLogger() }

    factory { MyRepository(logger = get(named("default"))) }
}

手動 DI(代替方法)

シンプルなサービスロケーター
// commonMain/kotlin/di/ServiceLocator.kt
object ServiceLocator {
    private val services = mutableMapOf<String, Any>()

    fun <T> get(key: String): T {
        return services[key] as T
    }

    fun register(key: String, service: Any) {
        services[key] = service
    }

    fun init() {
        register("repository", UserRepositoryImpl())
        register("api", UserApi())
    }
}
純粋な Kotlin DI
// commonMain/kotlin/di/AppContainer.kt
class AppContainer(
    private val platformService: PlatformService
) {
    val api: UserApi = UserApi()
    val database: AppDatabase = createDatabase()

    val userRepository: UserRepository by lazy {
        UserRepositoryImpl(api, database)
    }

    val homeViewModel: HomeViewModel by lazy {
        HomeViewModel(userRepository, platformService)
    }
}

DI を使用したテスト

// commonTest/kotlin/di/TestModule.kt
val testModule = module {
    single<MockUserService> { MockUserService() }
    single<UserService>(override = true) { get<MockUserService>() }
}

// In tests
@BeforeTest
fun setup() {
    startKoin { modules(testModule) }
}

@AfterTest
fun tearDown() {
    stopKoin()
}

覚えておいてください:DI によりテストが簡単になります。モックやフェイクを簡単に注入するために活用してください。

コメント