Upgrade to Pro — share decks privately, control downloads, hide ads and more …

KMPからPlarformコードを使いたい!

NItakan
January 28, 2025

 KMPからPlarformコードを使いたい!

NItakan

January 28, 2025
Tweet

Other Decks in Programming

Transcript

  1. ©2025 Kyash Inc. 自己紹介 object Nitakan { val id =

    "nitakan" val name = "KATO Masanori" val company = "Kyash Inc." val xAccount = "@nitakanworld" val githubAccount = "@nitakan" }
  2. ©2025 Kyash Inc. KMP→Platformコードの呼び出し • expect/actual ◦ KMPでexpect(interfaceみたいなもの)を定義し、各 Platform向けのactualを実装をす •

    インターフェース + 依存性注入(DI) ◦ KMPでインターフェースを定義し、各Platform上で実装 す どんな 手段があ のか
  3. ©2025 Kyash Inc. KMP→Platformコードの呼び出し interface PlatformRepository { fun getDataSync(): PlatformData

    // ← 通常の関数 suspend fun getDataAsync(): PlatformData // ← suspend関数 } interface PlatformRepositoryFactory { fun createClientRepository(): PlatformRepository } Kotlin Native側でRepositoryインターフェースを実装
  4. ©2025 Kyash Inc. KMP→Platformコードの呼び出し class DisplayDataViewModel( private val kmpRepository: KmpRepository,

    private val platformRepository: PlatformRepository, ) : ViewModel() { private val _displayData = MutableStateFlow<DisplayData>(DisplayData.loading()) @NativeCoroutinesState val displayData: StateFlow<DisplayData> = _displayData.asStateFlow() init { viewModelScope.launch { loadData() } } suspend fun loadData() { _displayData.emit(DisplayData.Loading) delay(1000) val platformSyncData = platformRepository.getDataSync() val kmp = kmpRepository.getKmpData() _displayData.emit(DisplayData.Loaded(platformSyncData, kmp)) val platformAyncData = platformRepository.getDataAsync() _displayData.emit(DisplayData.Loaded(platformAsyncData, kmp)) } } Kotlin Native側でViewModel実装
  5. ©2025 Kyash Inc. KMP→Platformコードの呼び出し class PlatformRepositoryImpl: PlatformRepository { override fun

    getDataSync(): PlatformData { return PlatformData("[Android]The client generated sync data") } override suspend fun getDataAsync(): PlatformData { delay(1000L) return PlatformData("[Android]The client generated async data") } } Android側のRepository実装
  6. ©2025 Kyash Inc. KMP→Platformコードの呼び出し class MainApp : Application() { override

    fun onCreate() { super.onCreate() initKoin( listOf( module { factory<PlatformRepository> { PlatformRepositoryImpl() } factory<PlatformRepositoryFactory> { PlatformRepositoryFactoryImpl(get()) } factory { DisplayDataViewModel(get(), get()) } } ) ) } } AndroidでViewModel生成時にRepositoryを渡す
  7. ©2025 Kyash Inc. KMP→Platformコードの呼び出し @Composable fun ListScreen() { val viewModel:

    DisplayDataViewModel = koinViewModel() val objects by viewModel.displayData.collectAsStateWithLifecycle(DisplayData.loading()) val coroutine = rememberCoroutineScope() Content( item = objects, onLoad = { coroutine.launch { viewModel.loadData() } }, ) } AndroidでViewModelを利用す
  8. ©2025 Kyash Inc. KMP→Platformコードの呼び出し import Shared class PlatformRepositoryImpl {} extension

    PlatformRepositoryImpl: PlatformRepository { func getDataSync() -> PlatformData { // ← 通常の関数の実装 PlatformData(value: "[iOS]The client generated sync data") } func getDataAsync() async throws -> PlatformData { // ← suspend関数の実装 _ = try await Task.sleep(nanoseconds: 1000_000_000) return PlatformData(value: "[iOS]The client generated async data") } } iOS側のRepository実装(Swift Concurrency)
  9. ©2025 Kyash Inc. KMP→Platformコードの呼び出し struct MainView: View { @Dependency(\.platformRepositoryFactory) var

    repositoryFactory: PlatformRepositoryFactory var body: some View { DisplayView(platformRepository: repositoryFactory.createPlatformRepository()) } } struct DisplayView: View { var viewModel: DisplayDataViewModel init(platformRepository: PlatformRepository) { viewModel = DisplayDataViewModel( kmpRepository: KmpRepositoryImpl(), platformRepository: platformRepository ) } } iOSでViewModel生成時にRepositoryを渡す
  10. ©2025 Kyash Inc. KMP→Platformコードの呼び出し struct DisplayView: View { @State private

    var displayData: DisplayData = DisplayData.Loading() var viewModel: DisplayDataViewModel init(platformRepository: PlatformRepository) { … } var body: some View { VStack(alignment: .center, spacing: 4) { switch (displayData) { case is DisplayData.Loading: Text("Loading...") … } } .task { do { for try await newValue in asyncSequence(for: viewModel.displayDataFlow) { self.displayData = newValue } } catch { print("Error collecting flow: \(error)") } } } } iOSでViewModelをUIで利用す
  11. ©2025 Kyash Inc. おわ に Platform固有コードを呼び出す方法には大きく分けて2つのアプローチがあ • expect/actual を使った Kotlin言語上での切

    分け • インターフェース+依存性注入(DI) を用いて、 Platform側の言語で実装 したコードをKMPへ 渡す方法 そ ぞ の使いどこ とメリット・デメリットを理解して、KMPライフを楽しみましょう 🍻🍻 まとめ
  12. ©2025 Kyash Inc. • Expected and actual declarations | Kotlin

    Multiplatform • Connect to platform-specific APIs | Kotlin Documentation • KMP-App-Template | GitHub Appendix