狠狠撸

狠狠撸Share a Scribd company logo
范聖佑
JetBrains Developer Advocate
JCConf Taiwan 2023
2023/10/06
在多平台間輕鬆共?業務邏輯與 UI 介?
從 otlin Multiplatform 到 Compose Multiplatform
範例程式碼
—
https://github.com/shengyou/kotlin-multiplatform-with-compose-demo
多平台開發痛點
—
? 各種型式的前端
多平台開發痛點
—
? 各種型式的前端
? 需要適應各種開發語?
多平台開發痛點
—
? 各種型式的前端
? 需要適應各種開發語?
? 重複實作的業務邏輯
DTO
Validation
Service
HTTP
Kotlin 編譯器設計
—
透過 Kotlin Multiplatform 共?業務邏輯
—
Server Web Desktop Android iOS
OS API Browser API OS API Android API iOS API
以 Kotlin Multiplatform 共?業務邏輯
使? Kotlin Multiplatform 技術開發雙平台 Mobile App
—
Server Web Desktop Android iOS
OS API Browser API OS API Android API iOS API
以 Kotlin Multiplatform 共?業務邏輯
Android View SwiftUI
使? Kotlin Multiplatform 技術開發雙平台 Mobile App
—
Kotlin Multiplatform Mobile
JCConf 2021 年的分享主題
—
https://youtu.be/SZGqMJXgOJg
?
有沒有辦法也共? UI 呢?
Compose Multiplatform
—
多平台
Jetpack Compose
建立?機應?程式介?
Compose for Web
Compose for Desktop
建立桌?應?程式介? 建立網路應?程式介?
透過 Compose Multiplatform 共? UI
—
Server Web Desktop Android iOS
OS API Browser API OS API Android API iOS API
以 Kotlin Multiplatform 共?業務邏輯
以 Compose Multiplatform 共? UI
Android View
Swing SwiftUI
Compose Multiplatform
JCConf 2022 年的分享主題
—
https://youtu.be/Ysh5CLkPiyE
?
可是我想要的是 iOS 啊…
KotlinConf’23 公佈?援 iOS!
—
https://youtu.be/FWVi4aV36d8
擴? Compose Multiplatform 版圖
—
Server Web Desktop Android iOS
OS API Browser API OS API Android API iOS API
以 Kotlin Multiplatform 共?業務邏輯
Android View
Swing SwiftUI
Compose Multiplatform
以 Compose Multiplatform 共? UI
以 Compose Multiplatform 共? UI
實作範例 - LoginScreen
—
實作範例 - HomeScreen
—
實作平台
—
? Android
? iOS
? Desktop
? Backend API
Backend
Desktop
Android
iOS
HTTPs Request/Response
JSON
Client
Server
?
實作成果展?
Demo TIME
?範重點
—
? 多平台共? UI - Compose Multiplatform
? 平台專? API - 偵測平台名稱
? 共享業務邏輯 - ViewModel、HTTP、Data Class
? 整合 Multiplatform 函式庫 - Ktor Client、Voyager、Kamel
? 後端 API 服務 - Ktor Server
????
實戰 Kotlin Multiplatform
建立開發環境
—
? Mac with macOS
? JDK
? Android Studio
? Kotlin Multiplatform Mobile Plugin
? Xcode + SDK
? Cocoapods
kdoctor 指令?具
—
透過 Homebrew 安裝的指令?
具,可?於檢查開發環境是否符
合 Kotlin Multiplatform Mobile 的
要求?
建立 Compose Multiplatform 專案
—
專案資料夾結構
—
? androidApp → Android 主程式
? iosApp → iOS 主程式
? desktopApp → Desktop 主程式
? shared → 共?程式碼
- commonMain → 多平台共?實作
- androidMain → Android 平台專?實作
- iosMain → iOS 平台專?實作
- desktopMain → Desktop 平台專?實作
? server → 後端 API 實作
?
多平台共? UI
—
實作??
—
object LoginScreen : Screen {
@Composable
override fun Content() {
/
/
.
.
.
}
}
各區塊元件化
—
Logo()
DemoOnText()
Spacer()
LoginForm(
/
*
.
.
.
*
/
)
Spacer()
PasswordForgetLink()
SignUpLink()
TextField(
value =
.
.
.
,
label = { Text(text = "Username") },
onValueChange = {
/
*
.
.
.
*
/
},
modifier =
.
.
.
,
)
TextField(
value =
.
.
.
,
label = { Text(text = "Password") },
visualTransformation =,
keyboardOptions =,
onValueChange = {
/
*
.
.
.
*
/
},
modifier =
.
.
.
,
)
Button(
onClick = {
/
*
.
.
.
*
/
},
shape = RoundedCornerShape(50.dp),
modifier =
.
.
.
,
) {
Text(text = "Login")
}
排版差異化 (Mobile)
—
Column(
modifier =
.
.
.
,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
/
/
.
.
.
Column(
modifier =
.
.
.
,
) {
/
/
.
.
.
}
/
/
.
.
.
}
Box(
modifier =
.
.
.
,
) {
/
/
.
.
.
}
排版差異化 (Desktop)
—
Row(
modifier =
.
.
.
,
horizontalArrangement = Arrangement.Center
) {
Column(
modifier =
.
.
.
,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
/
/
.
.
.
}
Column(
modifier =
.
.
.
,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
/
/
.
.
.
}
}
平台專? API
—
expect/actual 各平台實作
—
偵測平台版本
(expect 部份)
—
expect fun getPlatformName(): String
shared
- commonMain
- androidMain
- iosMain
- desktopMain
偵測平台版本
(actual 部份)
—
// androidMain
actual fun getPlatformName(): String =
"Andriod (API ${android.os.Build.VERSION.SDK_INT})"
// iosMain
actual fun getPlatformName(): String =
"iOS (${UIDevice.currentDevice.systemVersion})"
// desktopMain
actual fun getPlatformName(): String =
"Desktop (JVM ${Runtime.version()})"
shared
- commonMain
- androidMain
- iosMain
- desktopMain
偵測平台版本
(UI 部份)
— @Composable
fun DemoOnText() {
Text(
text = "Demo on ${getPlatformName()}",
style = TextStyle(
fontSize =
.
.
.
,
fontFamily =
.
.
.
,
color =
.
.
.
,
),
)
}
共享業務邏輯
—
API
發送 HTTP Request 接收 HTTP Response UI 換?
載入網路圖片
整合 Multiplatform 函式庫
—
? Ktor - HTTP Client
? kotlinx.serialization - JSON serialization/deserialization
? kotlinx.coroutines - Coroutine
? Voyager - Navigation、ViewModel
? Kamel - Asynchronous Media Loading
安裝相依套件
—
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.ktor:ktor-client-core:$ver")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$ver")
implementation("io.ktor:ktor-client-content-negotiation:$ver")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ver")
implementation("cafe.adriel.voyager:voyager-navigator:$ver")
implementation("io.ktor:ktor-client-core:$ver")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$ver")
implementation("io.ktor:ktor-client-content-negotiation:$ver")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ver")
implementation("media.kamel:kamel-image:$ver")
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-okhttp:$ver")
}
}
val iosMain by creating {
載入??
—
@Composable
fun MainApplication() {
val screen = when (getTarget()) {
Target.DESKTOP
-
>
DesktopLoginScreen
else
-
>
MobileLoginScreen
}
Navigator(screen)
}
實作 ViewModel
—
class LoginScreenModel :
StateScreenModel<LoginScreenModel.State>(State.Init) {
private val httpClient = HttpClient {
install(ContentNegotiation) {
json()
}
defaultRequest {
url(apiBaseUrl)
}
expectSuccess = true
}
sealed class State {
data object Init : State()
data class LoggedIn(val user: User) : State()
}
fun login(username: String, password: String) {
coroutineScope.launch {
val loginResponse: LoginResponse = httpClient.post("
.
.
.
") {
setBody(LoginRequest(username, password))
contentType(ContentType.Application.Json)
? 設定 HTTP Client
? 設定 State
? 發送 HTTP Req/Res
? 關閉 HTTP Client
使? ViewModel
—
object LoginScreen : Screen {
@Composable
override fun Content() {
val screenModel = rememberScreenModel { LoginScreenModel() }
val username = remember { mutableStateOf(TextFieldValue()) }
val password = remember { mutableStateOf(TextFieldValue()) }
Button(
onClick = {
screenModel.login(
username.value.text, password.value.text
)
},
shape = RoundedCornerShape(50.dp),
modifier =
.
.
.
,
) {
Text(text = "Login")
}
}
}
依狀態換?
—
object LoginScreen : Screen {
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { LoginScreenModel() }
val state by screenModel.state.collectAsState()
val username = remember { mutableStateOf(TextFieldValue()) }
val password = remember { mutableStateOf(TextFieldValue()) }
when (val loggedInState = state) {
is LoginScreenModel.State.Init
-
>
{}
is LoginScreenModel.State.LoggedIn
-
>
{
navigator.push(HomeScreen(loggedInState.user))
}
}
}
}
載入網路圖片
—
KamelImage(
resource = asyncPainterResource(
data = Url(user.profileImageUrl)
),
modifier =
.
.
.
,
contentDescription =
.
.
.
,
)
後端 API 服務
—
Server Client
語法簡單
輕量
適合做微服務
HTTP Client
?援多平台
搭配 kotlinx.serialization 做 SDK
共? Data Class
—
@Serializable
data class LoginRequest(
val username: String,
val password: String,
)
@Serializable
data class LoginResponse(
val result: Boolean,
val message: String,
val user: User? = null,
)
@Serializable
data class User(
val id: Int,
val username: String,
val password: String,
val email: String,
val displayName: String,
val profileImageUrl: String,
)
Ktor Server
—
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
install(ContentNegotiation) {
json()
}
// Ktor plugins
configureLogin()
}.start(wait = true)
}
實作 API Service
—
fun Application.configureLogin() {
routing {
post("...") {
// 接收 HTTP Request
// 反序列化成 Data Class
val req = call.receive<LoginRequest>()
// 登入驗證程式碼
// 回傳 HTTP Response
call.respond(
LoginResponse(
result = ...,
message = "...",
user = loggedInUser,
)
)
}
}
}
?
本?回顧
Kotlin Multiplatform 全版圖
—
Server Web Desktop Android iOS
OS API Browser API OS API Android API iOS API
以 Kotlin Multiplatform 共?業務邏輯
以 Compose Multiplatform 共? UI
Android View
Swing SwiftUI
實作範例
—
?範內容包括…
—
? 多平台共? UI - Compose Multiplatform
? 平台專? API - 偵測平台名稱
? 共享業務邏輯 - ViewModel、HTTP、Data Class
? 整合 Multiplatform 函式庫 - Ktor Client、Voyager、Kamel
? 後端 API 服務 - Ktor Server
Kotlin Multiplatform 優勢
—
? ?個語?跨多個平台
? 原?效能
? 可依需求調整共享程式碼的比例
對比其他解決?案
—
Kotlin Multiplatform ?態系
—
? 各平台設定檔
? Mobile 螢幕旋轉
? Network Retry
? Error Handling
? 前後端共?驗證邏輯
? 整合資料庫
? 導入更多的 Clean Architecture
可進?步完善的實作?向
—
Kotlin Multiplatform 學習資源
—
Kotlin Multiplatform 官??? Compose Multiplatform 官???
Compose Multiplatform 官?範例
—
實務案例
—
?機開發編年史 (攜程)
來?阿?巴巴及美團的
Kotlin Multiplatform 應?案例
#8
Kotlin 爐邊漫談 Podcast
https:
/
/
podcast.kotlin.tips/
#5
歡迎參加 Kotlin 社群
—
Line 群 Telegram 群
加入討論群取得最新資訊
tw.kotlin.tips
shengyou.fan@jetbrains.com
fb.me/shengyoufan
shengyoufan
@shengyou
在多平台間輕鬆共?業務邏輯與 UI 介?
從 otlin Multiplatform 到 Compose Multiplatform

More Related Content

[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 UI 介面