Slide 1

Slide 1 text

Get started with Kotlin Multiplatform Mobile 2021.03.05 YUMEMI.apk #3 Hiroyuki Kusu ( @hkusu_ )

Slide 2

Slide 2 text

About me

Slide 3

Slide 3 text

https://blog.jetbrains.com/ja/kotlin/2020/09/kotlin-multiplatform-mobile-goes-alpha-ja/

Slide 4

Slide 4 text

https://kotlinlang.org/docs/mobile/home.html KMM ͷυΩϡϝϯτ

Slide 5

Slide 5 text

Android Studio ༻ͷϓϥάΠϯ

Slide 6

Slide 6 text

৽ن KMM ϓϩδΣΫτ࡞੒༻ͷςϯϓϨʔτ

Slide 7

Slide 7 text

KMMϓϩδΣΫτͷ਽ܗ͕࡞੒͞ΕΔ

Slide 8

Slide 8 text

https://github.com/hkusu/KmmSampleApp ؆୯ͳαϯϓϧΛ࡞ͬͯΈͨ

Slide 9

Slide 9 text

6TF$BTF 3FQPTJUPSZ "QJ %BUB$MBTT "DUJWJUZ 7JFX.PEFM BOESPJE"QQ JPT"QQ ※ ࠓճ͸ݕূͰ͖͍ͯ·ͤΜ (JU)VC"1* TIBSFE

Slide 10

Slide 10 text

• ಈ࡞֬ೝͨ͠ϩʔΧϧ؀ڥ • Android Studio 4.1.2 • Android Studio Plugin • Kotlin: 1.4.31-release-Studio4.1-1 • Kotlin Multiplatform Mobile: 0.2.0-release-65-Studio4.1 • Ϗϧυπʔϧ΍ϥΠϒϥϦͷόʔδϣϯ౳͸ GitHub ͷίʔυͷํΛΈ͍ͯͩ͘͞

Slide 11

Slide 11 text

plugins { // ... id("kotlinx-serialization") } kotlin { // ... sourceSets { val commonMain by getting { dependencies { // ... implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0") implementation("io.ktor:ktor-client-core:1.5.2") implementation("io.ktor:ktor-client-serialization:1.5.2") } } // ... val androidMain by getting { dependencies { // ... implementation("io.ktor:ktor-client-android:1.5.2") } } // ... val iosMain by getting { dependencies { implementation("io.ktor:ktor-client-ios:1.5.2") } } // ... } } build.gradle.kts shared Coroutines + serialization + Ktor ͷಋೖ ※ ਖ਼֬ͳઃఆ͸ GitHub ͷίʔυͷํΛݟ͍ͯͩ͘͞

Slide 12

Slide 12 text

plugins { // ... id("kotlin-kapt") } kotlin { // ... sourceSets { // ... val androidMain by getting { dependencies { // ... implementation("com.google.dagger:hilt-android:2.31.2-alpha") // javax.annotation.Generated ͕ݟ͔ͭΒͳ͍Τϥʔ͕ग़ΔͷͰ.. compileOnly("javax.annotation:javax.annotation-api:1.3.2") } } // ... } } // ref: https://www.reddit.com/r/Kotlin/comments/ack2r6/problem_using_kapt_in_a_multiplatform_project/ dependencies { "kapt"("com.google.dagger:hilt-android-compiler:2.31.2-alpha") } build.gradle.kts shared ※ ਖ਼֬ͳઃఆ͸ GitHub ͷίʔυͷํΛݟ͍ͯͩ͘͞ Dagger Hilt ͷಋೖ(Androidͷํʹ͚ͩ)

Slide 13

Slide 13 text

buildscript { repositories { gradlePluginPortal() jcenter() google() mavenCentral() } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30") classpath("com.android.tools.build:gradle:4.1.2") classpath("org.jetbrains.kotlin:kotlin-serialization:1.4.30") classpath("com.google.dagger:hilt-android-gradle-plugin:2.31.2-alpha") } } allprojects { repositories { google() jcenter() mavenCentral() } } ϓϩδΣΫτϧʔτͷ build.gradle.kts

Slide 14

Slide 14 text

shared/commonMain Ktor ෦෼Ҏ֎͸ී௨ͷ Kotlin + Coroutines ͷίʔυͳͷͰ Ktor ෦෼͚ͩҎ߱Ͱઆ໌ ڞ௨ιʔε෦෼

Slide 15

Slide 15 text

internal object Libs { internal val httpClient: HttpClient by lazy { HttpClient { install(JsonFeature) { serializer = KotlinxSerializer(json = Json { ignoreUnknownKeys = true }) } } } } shared/commonMain Ktor ͷ HTTP ΫϥΠΞϯτͷੜ੒ͱ Kotlin serialization ͷઃఆ Android/iOS Ͱڞ௨ͷઃఆͰOK

Slide 16

Slide 16 text

internal class GitHubApi(private val httpClient: HttpClient) { suspend fun getUserList(): List { return httpClient.get("${BASE_URL}/users") } companion object { private const val BASE_URL = "https://api.github.com" } } shared/commonMain suspending function ʹ΋ରԠ APIͷఆٛ

Slide 17

Slide 17 text

@Module @InstallIn(SingletonComponent::class) internal object Module { @Provides fun provideGitHubApi(): GitHubApi { return GitHubApi(Libs.httpClient) } @Provides @Singleton fun provideUserRepository(gitHubApi: GitHubApi): UserRepository { return UserRepositoryImpl(gitHubApi) } @Provides fun provideGetUserUseCase(userRepository: UserRepository): GetUserUseCase { return GetUserUseCase(userRepository) } } shared/androidMain Dagger Hilt ͷϞδϡʔϧ ※ commonMain ʹ͸ Dagger ͸ແ͘ίϯετϥΫλΠϯδΣΫγϣϯͰ͖ͳ͍ͷͰ ͜͜ͰΠϯελϯεੜ੒ํ๏Λఆٛ (SharedͷதͷOSผ࣮૷)

Slide 18

Slide 18 text

plugins { id("com.android.application") kotlin("android") id("kotlin-kapt") id("dagger.hilt.android.plugin") } dependencies { implementation(project(":shared")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2") implementation("androidx.appcompat:appcompat:1.2.0") implementation("androidx.core:core-ktx:1.3.2") implementation("androidx.activity:activity-ktx:1.2.0") implementation("androidx.fragment:fragment-ktx:1.3.0") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.3.0") implementation("androidx.constraintlayout:constraintlayout:2.0.4") implementation("com.google.android.material:material:1.3.0") implementation("com.google.dagger:hilt-android:2.31.2-alpha") kapt("com.google.dagger:hilt-android-compiler:2.31.2-alpha") } android { compileSdkVersion(29) // ... build.gradle.kts androidApp ͓ͳ͡ΈͷϥΠϒϥϦୡ

Slide 19

Slide 19 text

plugins { id("com.android.application") kotlin("android") id("kotlin-kapt") id("dagger.hilt.android.plugin") } dependencies { implementation(project(":shared")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2") implementation("androidx.appcompat:appcompat:1.2.0") implementation("androidx.core:core-ktx:1.3.2") implementation("androidx.activity:activity-ktx:1.2.0") implementation("androidx.fragment:fragment-ktx:1.3.0") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.3.0") implementation("androidx.constraintlayout:constraintlayout:2.0.4") implementation("com.google.android.material:material:1.3.0") implementation("com.google.dagger:hilt-android:2.31.2-alpha") kapt("com.google.dagger:hilt-android-compiler:2.31.2-alpha") } android { compileSdkVersion(29) // ... build.gradle.kts androidApp Dagger Hilt

Slide 20

Slide 20 text

@HiltViewModel class MainViewModel @Inject constructor(getUser: GetUserUseCase) : ViewModel() { private val _userList: MutableLiveData> = MutableLiveData() val userList = _userList as LiveData> init { viewModelScope.launch { _userList.value = getUser() } } } ViewModel androidApp Dagger Hilt

Slide 21

Slide 21 text

@HiltViewModel class MainViewModel @Inject constructor(getUser: GetUserUseCase) : ViewModel() { private val _userList: MutableLiveData> = MutableLiveData() val userList = _userList as LiveData> init { viewModelScope.launch { _userList.value = getUser() } } } ViewModel androidApp shared ʹஔ͍ͨ UseCase Λ inject

Slide 22

Slide 22 text

• Android ෦෼ʹؔͯ͠͸ී௨ʹ࡞Εͦ͏ • Android ͔ΒݟΕ͹ී௨ͷϚϧνϞδϡʔϧߏ੒ • ڞ௨ιʔε෦෼ʹ͍ͭͯ͸.. • ϚϧνϓϥοτϑΥʔϜରԠͷϥΠϒϥϦΛར༻͢Δඞཁ͕͋Δ • ͜͜Λ iOS ޲͚ʹͲ͏ఏڙ͍͔͕ͯ͘͠ϙΠϯτʹͳΓͦ͏ • iOS ޲͚ʹ DI ͢ΔͳΒ KOIN ΍ Kodein Λར༻ͨ͠ํ͕Αͦ͞͏

Slide 23

Slide 23 text

Thank you ! @hkusu_