Skip to Content
Kotlin Multiplatform📚 Học KMPCấu trúc dự án KMP

Cấu trúc dự án KMP

Hiểu cấu trúc dự án là nền tảng để làm việc hiệu quả với Kotlin Multiplatform. Bài này giải thích chi tiết từng phần.

Tổng quan cấu trúc

MyKmpProject/ ├── build.gradle.kts # Root build config ├── settings.gradle.kts # Định nghĩa modules ├── gradle.properties # Gradle settings ├── local.properties # Local config (SDK paths) ├── composeApp/ # UI Module (Compose Multiplatform) │ ├── build.gradle.kts │ └── src/ │ ├── commonMain/ # UI code chung │ ├── androidMain/ # UI riêng Android │ ├── iosMain/ # UI riêng iOS │ └── desktopMain/ # UI riêng Desktop (optional) ├── shared/ # Shared Logic Module │ ├── build.gradle.kts │ └── src/ │ ├── commonMain/ # Business logic chung │ ├── commonTest/ # Tests chung │ ├── androidMain/ # Logic riêng Android │ ├── androidUnitTest/ # Android tests │ ├── iosMain/ # Logic riêng iOS │ └── iosTest/ # iOS tests └── iosApp/ # iOS Application ├── iosApp/ │ └── ContentView.swift ├── iosApp.xcodeproj └── Podfile

Source Sets - Khái niệm quan trọng nhất

Source Set là tập hợp code được compile cho một hoặc nhiều platforms.

Các Source Sets phổ biến

commonMain ┌──────────────┼──────────────┐ │ │ │ androidMain iosMain desktopMain │ │ │ ┌──────┴──────┐ │ │ │ │ iosArm64 iosX64 │ (iPhone) (Simulator)
Source SetCompile choVí dụ code
commonMainTất cả platformsData classes, interfaces
androidMainAndroidContext, SharedPreferences
iosMainiOS (Arm64 + X64)UIKit, CoreData
iosArm64MainiPhone thậtDevice-specific code
iosX64MainiOS Simulator (Intel Mac)Simulator-specific
iosSimulatorArm64MainiOS Simulator (M1/M2 Mac)Simulator-specific

File build.gradle.kts cho shared module

plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.androidLibrary) } kotlin { // Target platforms androidTarget { compilations.all { kotlinOptions { jvmTarget = "11" } } } // iOS targets listOf( iosX64(), // Intel Mac simulator iosArm64(), // Real iPhone iosSimulatorArm64() // M1/M2 Mac simulator ).forEach { it.binaries.framework { baseName = "shared" isStatic = true } } // Dependencies cho từng source set sourceSets { commonMain.dependencies { // Dependencies cho TẤT CẢ platforms implementation(libs.kotlinx.coroutines.core) implementation(libs.ktor.client.core) } commonTest.dependencies { implementation(libs.kotlin.test) } androidMain.dependencies { // Dependencies CHỈ cho Android implementation(libs.ktor.client.okhttp) } iosMain.dependencies { // Dependencies CHỈ cho iOS implementation(libs.ktor.client.darwin) } } } android { namespace = "com.example.shared" compileSdk = 34 defaultConfig { minSdk = 24 } }

Giải thích:

  • androidTarget(): Compile cho Android
  • iosX64(), iosArm64(), iosSimulatorArm64(): Compile cho iOS
  • sourceSets: Định nghĩa dependencies cho từng source set
  • binaries.framework: Tạo iOS framework để dùng trong Swift

Tổ chức code trong shared module

Pattern khuyến nghị

shared/src/commonMain/kotlin/ ├── data/ │ ├── model/ # Data classes │ │ └── User.kt │ ├── repository/ # Repository interfaces │ │ └── UserRepository.kt │ └── source/ │ ├── local/ # Local data sources │ └── remote/ # API clients ├── domain/ │ ├── model/ # Domain models │ └── usecase/ # Use cases └── platform/ └── Platform.kt # expect declarations

Ví dụ: User data class (chung cho tất cả platforms)

// shared/src/commonMain/kotlin/data/model/User.kt @Serializable data class User( val id: Long, val name: String, val email: String )

Ví dụ: Repository interface (chung)

// shared/src/commonMain/kotlin/data/repository/UserRepository.kt interface UserRepository { suspend fun getUsers(): List<User> suspend fun getUserById(id: Long): User? }

expect/actual - Kết nối các platforms

Khi cần code khác nhau cho từng platform, dùng expectactual:

Bước 1: Declare với expect (commonMain)

// shared/src/commonMain/kotlin/platform/Platform.kt // expect = "Tôi mong đợi có một implementation" expect class Platform() { val name: String } expect fun getPlatformName(): String

Bước 2: Implement với actual (androidMain)

// shared/src/androidMain/kotlin/platform/Platform.android.kt actual class Platform actual constructor() { actual val name: String = "Android ${android.os.Build.VERSION.SDK_INT}" } actual fun getPlatformName(): String = "Android"

Bước 3: Implement với actual (iosMain)

// shared/src/iosMain/kotlin/platform/Platform.ios.kt import platform.UIKit.UIDevice actual class Platform actual constructor() { actual val name: String = UIDevice.currentDevice.let { "${it.systemName()} ${it.systemVersion}" } } actual fun getPlatformName(): String = "iOS"

Sử dụng

// Ở bất kỳ đâu trong commonMain fun greet(): String { val platform = Platform() return "Hello from ${platform.name}!" } // → Android: "Hello from Android 34!" // → iOS: "Hello from iOS 17.0!"

Module composeApp

Module này chứa UI code dùng Compose Multiplatform:

composeApp/src/ ├── commonMain/kotlin/ │ ├── App.kt # Root composable │ ├── navigation/ # Navigation setup │ ├── ui/ │ │ ├── screen/ # Các màn hình │ │ ├── component/ # UI components │ │ └── theme/ # Theme, colors │ └── di/ # Dependency injection ├── androidMain/kotlin/ │ └── MainActivity.kt # Android entry point └── iosMain/kotlin/ └── MainViewController.kt # iOS entry point

iOS App structure

iosApp/ ├── iosApp/ │ ├── iOSApp.swift # App entry point │ └── ContentView.swift # SwiftUI wrapper ├── iosApp.xcodeproj # Xcode project └── Podfile # CocoaPods config

ContentView.swift - Dùng Compose trong SwiftUI

import SwiftUI import ComposeApp struct ContentView: View { var body: some View { ComposeView() .ignoresSafeArea(.all) } } struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { MainViewControllerKt.MainViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} }

📝 Tóm tắt

Khái niệmGiải thích
Source SetTập code cho một hoặc nhiều platforms
commonMainCode chung cho tất cả
androidMain/iosMainCode riêng từng platform
expectKhai báo cần implementation
actualImplementation cho từng platform
shared moduleChứa business logic
composeApp moduleChứa UI code

Tiếp theo

Tìm hiểu sâu hơn về Expect/Actual Pattern.

Last updated on