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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for NItakan NItakan
January 28, 2025

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

Avatar for NItakan

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