Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Jetpack for KMP

Jetpack for KMP

2024년 07월 20일 (토) I/O Extended Android in Korea 2024 발표자료입니다.
https://festa.io/events/5509

Sungyong An

July 20, 2024
Tweet

More Decks by Sungyong An

Other Decks in Programming

Transcript

  1. Intro Function, UseCase ١ਵ۽ ௏٘ܳ ܻ࠙ೞח Ѫ੉ ੗োझۣ׮. ೞա੄ ೐۽ં౟ী

    ߈ࠂغח ௏٘о ੓׮ݶ? fun doSomething() { ... // repeated codes ... // repeated codes ... } fun doSomething() { ... repeat() ... repeat() ... } fun repeat() { // repeated codes }
  2. Project A fun doSomething() { repeat() } Project B Intro

    زੌೠ ೒ۖಬ੉ۄݶ ۄ੉࠳۞ܻ۽ ௏٘ܳ ܻ࠙ೞৈ ઁҕೠ׮. ৈ۞ ೐۽ં౟ী ߈ࠂغח ௏٘о ੓׮ݶ? Library fun repeat() { // repeated codes } fun doSomething() { repeat() }
  3. Intro JVM਷ ੗߄ ߄੉౟௏٘ܳ ೧ࢳೞৈ যڃ ೒ۖಬীࢲب زੌೞѱ प೯ೡ ࣻ

    ੓׮. ৈ۞ ೒ۖಬী زੌೠ ௏٘ ࢎਊೞח ߑߨ? Link: h tt ps://en.wikipedia.org/wiki/Java_vi rt ual_machine JVM OS Windows Linux macOS Hardware Program Program ... ...
  4. Intro Kotlin Multiplatformਸ ࢎਊೞח Ѫب ೞա੄ ߑߨ! ৈ۞ ೒ۖಬী ߈ࠂغח

    ௏٘о ੓׮ݶ? Link: h tt ps://www.jetbrains.com/kotlin-multipla tf orm/ Client Multiplatform Common Multiplatform Server Android iOS Desktop Web
  5. // commonMain fun greet(platform: String = "Kotlin Multiplatform"): String {

    return "Hello, $platform!" } // androidMain greet(platform = "Android") // iosMain greet(platform = "iOS") // desktopMain greet(platform = "Desktop") ✅
  6. // commonMain fun greet(): String { return "Hello, ${platform()}!" }

    // androidMain fun platform() = "Android" // iosMain fun platform() = "iOS" // desktopMain fun platform() = "Desktop" ❌
  7. Section 1 commonMain/ androidMain/ iosMain/ desktopMain/ expect - actual expect

    actual actual actual ✅ Link: h tt ps://kotlinlang.org/docs/multipla tf orm-expect-actual.html
  8. // commonMain fun greet(): String { return "Hello, ${platform()}!" }

    expect fun platform() // androidMain actual fun platform() = "Android" // iosMain actual fun platform() = "iOS" // desktopMain actual fun platform() = “Desktop" ✅
  9. // commonMain fun greet(platform: Platform): String { return "Hello, ${platform.name}!"

    } expect class Platform { val name: String } // androidMain actual class Platform { actual val name: String get() = "Android" } // iosMain actual class Platform { actual val name: String get() = "iOS" } // desktopMain actual class Platform { actual val name: String get() = "Desktop" } ⚠ Beta
  10. Supported Platforms Section 1 Link: h tt ps://www.jetbrains.com/help/kotlin-multipla tf orm-dev/suppo

    rt ed-pla tf orms.html Android Stable iOS Stable Desktop (JVM) Stable Server-side (JVM) Stable Web based on Kotlin/Wasm Alpha Web based on Kotlin/JS Stable watchOS, tvOS Best effort
  11. Section 1 Targets kotlin { androidTarget() iosX64() iosArm64() iosSimulatorArm64() }

    common jvm android native js androidNative apple mingw linux ios macos tvos watchos iosSimulatorArm64 iosX64 iosArm64 Link: h tt ps://kotlinlang.org/docs/multipla tf orm-discover-project.html#targets
  12. kotlin { sourceSets { commonMain.dependencies { implementation(libs.ktor.client.core) } androidMain.dependencies {

    implementation(libs.ktor.client.okhttp) } iosMain.dependencies { implementation(libs.ktor.client.darwin) } } } Source sets Section 1 Link: h tt ps://kotlinlang.org/docs/multipla tf orm-discover-project.html#source-sets commonMain androidMain iosMain
  13. sourceSets { val commonMain by getting { dependencies { ...

    } } val jvmAndroidMain by creating { dependsOn(commonMain) dependencies { ... } } val androidMain by getting { dependsOn(jvmAndroidMain) } val desktopMain by getting { dependsOn(jvmAndroidMain) } } Additional source sets Section 1 desktopMain jvmAndroidMain androidMain commonMain
  14. // Kotlin Multiplatform - build.gradle.kts plugins { id("com.android.application") id("org.jetbrains.kotlin.multiplatform") }

    android { ... } kotlin { androidTarget() sourceSets { commonMain.dependencies { ... } androidMain.dependencies { ... } } }
  15. 🗂 org/jetbrains/kotlinx/ 🗂 kotlinx-coroutines-core/1.8.1/ - kotlinx-coroutines-core -1.8.1.jar 🗂 kotlinx-coroutines-core-jvm/1.8.1/ -

    kotlinx-coroutines-core-jvm -1.8.1.jar 🗂 kotlinx-coroutines-core-iosarm64/1.8.1/ - kotlinx-coroutines-core-iosarm64 -1.8.1.klib KMP Library Section 1 Non-platform source set’s Refer to h tt ps://www.youtube.com/watch?v=vU48FVzBLtc
  16. kotlin { sourceSets { commonMain.dependencies { implementation("org.jetbrains.kotlinx: kotlinx-coroutines-core :1.8.1") }

    jvmMain.dependencies { implementation("org.jetbrains.kotlinx: kotlinx-coroutines-core-jvm :1.8.1") } iosArm64Main.dependencies { implementation("org.jetbrains.kotlinx: kotlinx-coroutines-core-iosarm64 :1.8.1”) } } } ❌
  17. kotlin { sourceSets { commonMain.dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") } // jvmMain.dependencies

    { // implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1") // } // iosArm64Main.dependencies { // implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-iosarm64:1.8.1") // } } }
  18. kotlin { sourceSets { commonMain.dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") } } }

    // jvmMain -> "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1" // iosArm64Main -> "org.jetbrains.kotlinx:kotlinx-coroutines-core-iosarm64:1.8.1" ✅
  19. Android Jetpack Section 2 Link: h tt ps://developer.android.com/jetpack Collection Compose

    DataStore Activity ConstraintLayout Annotation AppCompat Browser Camera Fragment Core Glance Lifecycle ViewPager Navigation Paging Preference RecyclerView Room ViewModel Window WorkManager
  20. App Domain UI App Architecture Section 2 Link: h tt

    ps://developer.android.com/topic/architecture Data Collection Compose DataStore Activity ConstraintLayout Annotation AppCompat Browser Fragment Core Lifecycle ViewPager Navigation Paging Preference RecyclerView Room ViewModel Window WorkManager
  21. App Domain UI Jetpack supports KMP Section 2 Data Collection

    Annotation Core Link: h tt ps://developer.android.com/kotlin/multipla tf orm DataStore Paging Preference Room WorkManager Compose Activity ConstraintLayout AppCompat Browser Fragment Lifecycle ViewPager Navigation RecyclerView ViewModel Window
  22. App Domain UI Jetpack supports KMP Section 2 Data Collection

    Annotation Core Link: h tt ps://developer.android.com/kotlin/multipla tf orm DataStore Paging Preference Room WorkManager Compose Activity ConstraintLayout AppCompat Browser Fragment Lifecycle ViewPager Navigation RecyclerView ViewModel Window
  23. UI Domain Data To share Business Logic! Section 2 Collection

    Link: h tt ps://developer.android.com/kotlin/multipla tf orm DataStore Room Repository UseCase Compose ViewModel Lifecycle
  24. • ArrayMap (JVM only) ਸ ઁ৻ೞݶ, ؀ࠗ࠙੄ ஸ۩࣌ਸ Ӓ؀۽ ࢎਊೡ

    ࣻ ੓׮. • SparseArray, LruCache, ... • ArrayMap ؀উਵ۽ ScatterMap ١ਸ ࢎਊೡ ࣻ ੓׮. • Romain Guy “A Better Hash Map — 1” ଵҊ https://www.romainguy.dev/posts/2024/a-better-hashmap/ Collection Section 2 Link: h tt ps://developer.android.com/jetpack/androidx/releases/collection
  25. DataStore Section 2 androidx.datastore:datastore-core ✅ ✅ ✅ androidx.datastore:datastore-core-okio ✅ ✅

    ✅ androidx.datastore:datastore-preferences-core ✅ ✅ ✅ androidx.datastore:datastore-preferences ✅ ❌ ✅ module Android iOS Desktop Link: h tt ps://developer.android.com/jetpack/androidx/releases/datastore
  26. // Android // Setup dependencies { implementation("androidx.datastore:datastore-preferences:1.1.1") } // Create

    val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings") Link: h tt ps://developer.android.com/topic/libraries/architecture/datastore
  27. // Android // Read val EXAMPLE_COUNTER = intPreferencesKey("example_counter") val exampleCounterFlow:

    Flow<Int> = context.dataStore.data.map { preferences -> preferences[EXAMPLE_COUNTER] ?: 0 } // Write suspend fun incrementCounter() { context.dataStore.edit { settings -> val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0 settings[EXAMPLE_COUNTER] = currentCounterValue + 1 } } Link: h tt ps://developer.android.com/topic/libraries/architecture/datastore
  28. // Kotlin Multiplatform // Create import okio.Path.Companion.toPath val dataStoreFileName =

    "datastore-name.preferences_pb" val datastore: DataStore<Preferences> = PreferenceDataStoreFactory.createWithPath( produceFile = { produceFilePath().toPath() }, ) Link: h tt ps://developer.android.com/kotlin/multipla tf orm/datastore // androidMain fun produceFilePath(): String { return context.filesDir.resolve(dataStoreFileName).absolutePath } // iosMain fun produceFilePath(): String = “${fileDirectory()}/$dataStoreFileName"
  29. Room, SQLite Section 2 Link: h tt ps://developer.android.com/kotlin/multipla tf orm

    androidx.room:room-common ✅ ✅ ✅ androidx.room:room-runtime ✅ ✅ ✅ androidx.room:room-migration ✅ ✅ ✅ androidx.sqlite:sqlite ✅ ✅ ✅ androidx.sqlite:sqlite-framework ✅ ✅ ❌ androidx.sqlite:sqlite-bundled ✅ ✅ ✅ module Android iOS Desktop ❓ ❓
  30. // Android // Setup dependencies { implementation("androidx.room:room-runtime:2.6.0") ksp("androidx.room:room-compiler:2.6.0") } //

    Entity @Entity data class User( @PrimaryKey(autoGenerate = true) val id: Long = 0, @ColumnInfo(name = "name") val name: String, ) Link: h tt ps://developer.android.com/training/data-storage/room
  31. // Android // Data Access Object @Dao interface UserDao {

    @Query("SELECT * FROM user”) fun getAll(): List<User> @Insert fun insertAll(vararg users: User) } // Database @Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao } Link: h tt ps://developer.android.com/training/data-storage/room
  32. // Android // Create val dbFileName = "database-name" val db

    = Room.databaseBuilder( applicationContext, AppDatabase::class.java, dbFileName, ).build() // Usage val userDao = db.userDao() val users: List<User> = userDao.getAll() Link: h tt ps://developer.android.com/training/data-storage/room
  33. // Kotlin Multiplatform // Setup commonMain.dependencies { implementation("androidx.room:room-runtime:2.7.0-alpha04") implementation("androidx.sqlite:sqlite-bundled:2.5.0-alpha04") }

    dependencies { add("kspAndroid", "androidx.room:room-compiler:2.7.0-alpha04") add("kspIosSimulatorArm64", "androidx.room:room-compiler:2.7.0-alpha04") add("kspIosX64", "androidx.room:room-compiler:2.7.0-alpha04") add("kspIosArm64", "androidx.room:room-compiler:2.7.0-alpha04") } // Entity (Same) // Database (Same) Link: h tt ps://developer.android.com/kotlin/multipla tf orm/room
  34. // Kotlin Multiplatform // Data Access Object @Dao interface UserDao

    { // @Query("SELECT * FROM user") fun getAll(): List<User> @Query("SELECT * FROM user") fun getAll(): Flow<List<User>> // @Insert fun insertAll(vararg users: User) @Insert suspend fun insertAll(vararg users: User) } Link: h tt ps://developer.android.com/kotlin/multipla tf orm/room
  35. // Kotlin Multiplatform // Usage - androidMain val dbFileName =

    "database-name.db" val db = Room.databaseBuilder<AppDatabase>( context = applicationContext, name = application.getDatabasePath(dbFileName), ) .setDriver(BundledSQLiteDriver()) // or AndroidSQLiteDriver .setQueryCoroutineContext(Dispatchers.IO) .build() Link: h tt ps://developer.android.com/kotlin/multipla tf orm/room ❓
  36. Framework SQLite Android Common Native Android Native sqlite3.h android.database. SQLiteDatabase

    Android SQLite Driver Native SQLite Driver room-runtime sqlite-framework
  37. Link: h tt ps://developer.android.com/kotlin/multipla tf orm/room // Kotlin Multiplatform //

    Usage - iosMain val dbFileName = "database-name.db" val db = Room.databaseBuilder<AppDatabase>( name = "${fileDirectory()}/$dbFileName", factory = { AppDatabase::class.instantiateImpl() } ) .setDriver(BundledSQLiteDriver()) // or NativeSQLiteDriver .setQueryCoroutineContext(Dispatchers.IO) .build()
  38. forked from androidx/androidx Compose Multiplatform Core Section 3 Link: h

    tt ps://github.com/JetBrains/compose-multipla tf orm-core commonMain androidMain jbMain ... nativeMain desktopMain … webMain or skikoMain Jetpack Compose Compose Multiplatform
  39. // Jetpack Compose dependencies { implementation("androidx. compose.runtime :runtime:1.6.7") implementation("androidx. compose.ui

    :ui:1.6.7") implementation("androidx. compose.foundation :foundation:1.6.7") implementation("androidx. compose.animation :animation:1.6.7") implementation("androidx. compose.material :material:1.6.7") implementation("androidx. compose.material3 :material3:1.2.1") } Link: h tt ps://developer.android.com/jetpack/androidx/releases/compose#versions
  40. // Compose Multiplatform commonMain.dependencies { implementation("org.jetbrains. compose.runtime :runtime:1.6.11") implementation("org.jetbrains. compose.ui

    :ui:1.6.11") implementation("org.jetbrains. compose.foundation :foundation:1.6.11") implementation("org.jetbrains. compose.animation :animation:1.6.11") implementation("org.jetbrains. compose.material :material:1.6.11") implementation("org.jetbrains. compose.material3 :material3:1.6.11") } Link: h tt ps://www.jetbrains.com/help/kotlin-multipla tf orm-dev/compose-compatibility-and-versioning.html
  41. // Compose Multiplatform plugins { id("org.jetbrains.compose") version "1.6.11" } commonMain.dependencies

    { implementation( compose.runtime ) implementation( compose.ui ) implementation( compose.foundation ) implementation( compose.animation ) implementation( compose.material ) implementation( compose.material3 ) }
  42. // Android class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState:

    Bundle?) { super.onCreate(savedInstanceState) setContent { // ComposeView App() } } } // Desktop fun main() = application { Window(...) { // ComposeWindow App() } }
  43. // iOS fun MainViewController(): UIViewController { return ComposeUIViewController { App()

    } } @main struct iOSApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { var body: some View { ComposeView() } } struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { MainViewControllerKt.MainViewController() } } Link: h tt ps://www.jetbrains.com/help/kotlin-multipla tf orm-dev/compose-swi ft ui-integration.html
  44. // Jetpack Compose android { android.buildFeatures.resValues = true // default

    } import com.example.kmp.R Image( painterResource(R.drawable.compose_multiplatform), contentDescription = null, )
  45. // Compose Multiplatform commonMain.dependencies { implementation(compose.components.resources) } import kotlinproject.composeapp.generated.resources.Res import

    kotlinproject.composeapp.generated.resources.compose_multiplatform Image( painterResource(Res.drawable.compose_multiplatform), contentDescription = null, )
  46. Lifecycle Section 3 androidx.lifecycle:lifecycle-common ✅ ✅ ✅ ❌ androidx.lifecycle:lifecycle-runtime ✅

    ✅ ✅ ❌ androidx.lifecycle:lifecycle-runtime-compose ✅ ❌ ✅ ❌ org.jetbrains.androidx.lifecycle:lifecycle-common ✅ ✅ ✅ ✅ org.jetbrains.androidx.lifecycle:lifecycle-runtime ✅ ✅ ✅ ✅ org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose ✅ ✅ ✅ ✅ Android iOS Desktop Web module Link: h tt ps://www.jetbrains.com/help/kotlin-multipla tf orm-dev/compose-lifecycle.html
  47. Lifecycle Common Section 3 INITIALIZED DESTROYED CREATED STARTED RESUMED INITIALIZED

    DESTROYED CREATED STARTED RESUMED States Events States ON_PAUSE ON_RESUME ON_STOP ON_START ON_DESTROY ON_CREATE
  48. Lifecycle to other platforms Section 3 Desktop iOS ViewControllerBasedLifecycleOwner.uikit.kt ComposeUIViewController.uikit.kt

    ✅ ComposeContainer.uikit.kt ComposeContainer.desktop.kt ✅ ComposeWindow.desktop.kt ComposeWindowPanel.desktop.kt LocalLifecycleOwner
  49. ViewModel Section 3 Link: h tt ps://www.jetbrains.com/help/kotlin-multipla tf orm-dev/compose-viewmodel.html androidx.lifecycle:lifecycle-viewmodel

    ✅ ✅ ✅ ❌ androidx.lifecycle:lifecycle-viewmodel-compose ✅ ❌ ✅ ❌ org.jetbrains.androidx.lifecycle:lifecycle-viewmodel ✅ ✅ ✅ ✅ org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose ✅ ✅ ✅ ✅ Android iOS Desktop Web module
  50. // Jetpack Compose dependencies { implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0") } @Composable fun CupcakeApp(

    viewModel: OrderViewModel = viewModel(), ) { ... } class OrderViewModel : ViewModel() { val uiState: StateFlow<OrderUiState> = ... }
  51. // Compose Multiplatform commonMain.dependencies { implementation("org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0") } @Composable fun CupcakeApp(

    viewModel: OrderViewModel = viewModel { OrderViewModel() }, ) { ... } class OrderViewModel : ViewModel() { val uiState: StateFlow<OrderUiState> = ... }
  52. // Compose Multiplatform (Desktop) class OrderViewModel : ViewModel() { fun

    doSomething() { viewModelScope.launch { ... } } } // build.gradle.kts commonMain.dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") } val desktopMain by getting desktopMain.dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.8.1") } // Dispatchers.Main.immediate
  53. @Composable NavHost NavBackStackEntry @Composable ✅ ViewModelStoreOwner Section 3 ComposeView ComposeUIViewController

    ComposeWindow ComponentActivity ✅ ✅ ✅ LocalViewModelStoreOwner NavBackStackEntry ✅ @Composable
  54. Navigation Section 3 Link: h tt ps://www.jetbrains.com/help/kotlin-multipla tf orm-dev/compose-navigation-routing.html androidx.navigation:navigation-*

    ✅ ❌ ❌ ❌ org.jetbrains.androidx.navigation:navigation-common ✅ ✅ ✅ ✅ org.jetbrains.androidx.navigation:navigation-runtime ✅ ✅ ✅ ✅ org.jetbrains.androidx.navigation:navigation-compose ✅ ✅ ✅ ✅ Android iOS Desktop Web module
  55. // Jetpack Compose dependencies { implementation("androidx.navigation:navigation-compose:2.7.7") } // Compose Multiplatform

    commonMain.dependencies { implementation("org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha07") }
  56. // Jetpack Compose // Compose Multiplatform val navController: NavHostController =

    rememberNavController() NavHost(navController, startDestination = "list") { composable("list") { ListScreen(onItemClick = { id -> navController.navigate("detail/$id") }) } composable( "detail/{id}", arguments = listOf(navArgument("id") { type = NavType.LongType }) ) { backStackEntry -> val id = backStackEntry.arguments?.getLong("id")!! DetailScreen(id = id) } }
  57. JetBrains Internal ۄ੉࠳۞ܻ Section 3 androidx.annotation:annotation ✅ ✅ ✅ ❌

    androidx.collection:collection ✅ ✅ ✅ ❌ org.jetbrains.compose.annotation-internal:annotation ✅ ✅ ✅ ✅ org.jetbrains.compose.collection-internal:collection ✅ ✅ ✅ ✅ Android iOS Desktop Web module
  58. UI Compose ViewModel Lifecycle Domain Data To share UI Logic!

    Collection* DataStore Room Repository UseCase Compose ViewModel Lifecycle Collection* Navigation Section 3