KMM
(Shengyou Fan)
JCConf Taiwan 2021
2021/11/19
Photo by Arnel Hasanovic on Unsplash
—
Mobile https://github.com/shengyou/KmmKungFu
Server https://github.com/shengyou/password-checker
Kotlin
—
• General-purpose
• Static typing
• OOP + FP
• Developed by
JetBrains
• Open Source
(Apache 2.0)
https://kotlinlang.org/
Kotlin
—
Browser
Kotlin/JS
Server
Kotlin/JVM
iOS
Kotlin/Native
Android
Kotlin/JVM
—
KMM
—
expect / actual
—
—
KMM opt.1
—
Android
iOS
Android Studio
Xcode
KMM plugin
Simulator
AVD
KMM opt.2
—
Android
iOS
AppCode
KMM plugin

For AppCode
Simulator
AVD
+
KMM Plugin
—
KMM (Step 1)
—
KMM (Step 2)
—
KMM (Step 3)
—
KMM (Step 4)
—
—
• androidApp
• iosApp
• shared
- commonMain
- androidMain
- iosMain
•
-
• API
-
• Multiplatform
- HTTP API
—
—
pt.1
( )
—
class PasswordGenerator {
private val chars = ...
fun generate(length: Int): String {
// ...
}
}
pt.2
(Android )
— class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val passwordTextView: TextView = findViewById(...)
val generateButton: Button = findViewById(...)
generateButton.setOnClickListener {
passwordTextView.text = PasswordGenerator()
.generate(7)
}
}
}
pt.3
(iOS )
— struct ContentView: View {
@State private var passwordText = "!"
var body: some View {
Text(passwordText)
Button(action: {
passwordText = PasswordGenerator()
.generate(length: 10)
}) {
Text(" ")
}
}
}
—
pt.1
(expect )
—
// shared class
class Detector {
fun detect(): String {
return Platform().platform
}
}
// expect
expect class Platform() {
val platform: String
}
pt.2
(actual )
—
// androidMain
actual class Platform actual constructor() {
actual val platform: String =
"Android version" +
android.os.Build.VERSION.SDK_INT
}
// iosMain
actual class Platform actual constructor() {
actual val platform: String =
UIDevice.currentDevice.systemName() +
" version " +
UIDevice.currentDevice.systemVersion
}
pt.3
(Android )
— class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val messageTextView: TextView = findViewById(…)
messageTextView.text = Detector().detect()
}
}
pt.4
(iOS )
— struct ContentView: View {
@State private var messageText = Detector().detect()
var body: some View {
Text(messageText)
.font(.subheadline)
.foregroundColor(messageTextColor)
}
}
API
—
—
Mobile API Server
(KMM + Ktor Client) (Ktor Server)
API pt.1
( )
—
kotlin {
// ...
sourceSets {
val commonMain by getting {
dependencies {
// Ktor
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
// Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$ver")
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:$ktorVersion")
}
}
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-ios:$ktorVersion")
}
}
}
}
1. Ktor Client
2. kotlinx.serialization
// shared
expect fun httpClient(config: HttpClientConfig<*>.() -> Unit): HttpClient
// androidMain
actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Android) {
config(this)
engine {
connectTimeout = 100_000
socketTimeout = 100_000
}
}
// iosMain
actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Ios) {
config(this)
engine {
configureRequest {
setAllowsCellularAccess(true)
}
}
}
API pt.2
(expect / actual )
—
@Serializable
data class InspectRequest(
val password: String
)
@Serializable
data class InspectResponse(
val result: Boolean,
val message: String
)
API pt.4
(DTO )
—
class Validator {
private val httpClient = httpClient {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
}
suspend fun inspect(password: String): InspectResponse {
return httpClient.post("...") {
accept(ContentType.Application.Json)
contentType(ContentType.Application.Json)
body = InspectRequest(password)
}
}
}
API pt.3
( )
—
class MainActivity : AppCompatActivity() {
private val validator = Validator()
private val mainScope = MainScope()
val inspectButton: Button = findViewById(...)
inspectButton.setOnClickListener {
mainScope.launch {
kotlin.runCatching {
validator.inspect(...)
}.onSuccess {
messageTextView.text = it.message
}
}
}
}
API pt.5
(Android )
—
struct HttpClientView: View {
let validator = Validator()
func validate() {
validator.inspect(password: pw) { response, error in
if let response = response {
self.messageText = response.message
} else if let error = error {
self.messageText = "Error: (error)"
}
}
}
var body: some View {
Button(action: {
validate()
}) {
// ...
}
}
}
API pt.6
(iOS )
—
Browser
Kotlin/JS
Server
Kotlin/JVM
iOS
Kotlin/Native
Android
Kotlin/JVM
- Kotlin
—
• KMM
- UI - UI
- - expect/actual
-
-
- Multiplatform
- KMM
—
• Ktor (HTTP Client)
• kotlinx.serialization
• kotlinx.coroutines
• kotlinx.atomicfu
• SQLDelight
• KaMP Kit
• moko libraries
KMM
—
—
https://kotlinlang.org/docs/mobile/samples.html
KMM
—
https://kotlinlang.org/lp/mobile/case-studies/
Kotlin
—
kotlin.tips
https://tw.kotlin.tips
Tips
—
https://tw.intellij.tips
Kotlin
—
Line Telegram
—
Coding
Kraftsman
(Shengyou Fan)
shengyou.fan@jetbrains.com
Q&A
—
Kotlin Multiplatform Mobile

以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用