Kotlin Multiplatform - UI共有なし
このチュートリアルでは、Android アプリケーションを作成し、Koin の依存関係注入(Dependency Injection)を使用してコンポーネントを取得する方法を学びます。 チュートリアルの所要時間は約 15 分 です。
NOTE
更新日 - 2024-10-21
コードを入手する
アプリケーションの概要
このアプリケーションの目的は、ユーザーのリストを管理し、共有の Presenter を使用してネイティブ UI に表示することです。
Users -> UserRepository -> 共有 Presenter -> ネイティブ UI
「User」データ
すべての共通/共有コードは
sharedGradle プロジェクト内にあります。
ユーザーのコレクションを管理します。データクラスは以下の通りです:
data class User(val name : String)ユーザーのリストを管理(ユーザーの追加や名前による検索)するための「Repository」コンポーネントを作成します。以下は、UserRepository インターフェースとその実装です:
interface UserRepository {
fun findUser(name : String): User?
fun addUsers(users : List<User>)
}
class UserRepositoryImpl : UserRepository {
private val _users = arrayListOf<User>()
override fun findUser(name: String): User? {
return _users.firstOrNull { it.name == name }
}
override fun addUsers(users : List<User>) {
_users.addAll(users)
}
}共有 Koin モジュール
Koin モジュールを宣言するには module 関数を使用します。Koin モジュールは、注入されるすべてのコンポーネントを定義する場所です。
最初のコンポーネントを宣言しましょう。UserRepositoryImpl のインスタンスを作成して、UserRepository のシングルトンを作成します。
module {
singleOf(::UserRepositoryImpl) { bind<UserRepository>() }
}共有 Presenter
ユーザーを表示するための Presenter コンポーネントを記述します:
class UserPresenter(private val repository: UserRepository) {
fun sayHello(name : String) : String{
val foundUser = repository.findUser(name)
val platform = getPlatform()
return foundUser?.let { "Hello '$it' from ${platform.name}" } ?: "User '$name' not found!"
}
}
UserRepositoryはUserPresenterのコンストラクターで参照されています。
Koin モジュールで UserPresenter を宣言します。インスタンスをメモリに保持し続けず、ネイティブ側のシステムに保持させるために、factoryOf 定義として宣言します:
val appModule = module {
singleOf(::UserRepositoryImpl) { bind<UserRepository>() }
factoryOf(::UserPresenter)
}NOTE
Koin モジュールは、iOS 側から initKoin() 関数を使用して簡単に実行できるように、実行可能な関数(ここでは appModule)として利用可能です。
ネイティブコンポーネント
以下のネイティブコンポーネントが Android と iOS で定義されています:
interface Platform {
val name: String
}
expect fun getPlatform(): Platform両方のプラットフォームでローカルのプラットフォーム実装を取得します。
Android でのインジェクション
Android アプリのコードはすべて
androidAppGradle プロジェクト内にあります。
UserPresenter コンポーネントが作成され、その際に UserRepository インスタンスが解決されます。これを Activity で取得するために、Compose 関数の koinInject を使用して注入しましょう:
// App() 内
val greeting = koinInject<UserPresenter>().sayHello("Koin")
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Image(painterResource(Res.drawable.compose_multiplatform), null)
Text("Compose: $greeting")
}これでアプリの準備は完了です。
INFO
koinInject() 関数を使用すると、Android Compose のランタイムで Koin インスタンスを取得できます。
Android アプリケーションで Koin を開始する必要があります。Compose のアプリケーション関数 App 内で KoinApplication() 関数を呼び出すだけです:
fun App() {
KoinApplication(application = koinAndroidConfiguration(LocalContext.current)){
// ...
}
}共有の KMP 設定から、Android 用の Koin 設定を収集します:
// Android 設定
fun koinAndroidConfiguration(context: Context) : KoinAppDeclaration = {
androidContext(context)
androidLogger()
koinSharedConfiguration()
}NOTE
Compose から現在の Android context を取得するには LocalContext.current を使用します。
そして、共有の KMP 設定は以下の通りです:
// 共通設定
fun koinSharedConfiguration() : KoinAppDeclaration = {
modules(appModule)
}INFO
modules() 関数は、指定されたモジュールのリストをロードします。
iOS でのインジェクション
iOS アプリのコードはすべて
iosAppフォルダ内にあります。
UserPresenter コンポーネントが作成され、その際に UserRepository インスタンスが解決されます。これを ContentView で取得するには、iOS 用に Koin の依存関係を取得するための関数を作成する必要があります:
// Koin.kt
fun getUserPresenter() : UserPresenter = KoinPlatform.getKoin().get()これで完了です。iOS 側から KoinKt.getUserPresenter().sayHello() 関数を呼び出すことができます。
import Shared
struct ContentView: View {
// ...
let greet = KoinKt.getUserPresenter().sayHello(name: "Koin")
}iOS アプリケーションで Koin を開始する必要があります。Kotlin の共有コードでは、initKoin() 関数を使用して共有設定を利用できます。 最後に、iOS のメインエントリで、上記のヘルパー関数を呼び出す KoinAppKt.doInitKoin() 関数を呼び出します。
@main
struct iOSApp: App {
init() {
KoinAppKt.doInitKoin()
}
//...
}