讓您的 Android 應用程式在 iOS 上運作 – 教學
本教學將展示如何讓現有的 Android 應用程式成為跨平台應用程式,使其能在 Android 和 iOS 上運作。您將能夠在同一個地方同時為 Android 和 iOS 編寫程式碼。
本教學使用一個範例 Android 應用程式,該應用程式具有一個用於輸入使用者名稱和密碼的單一畫面。憑據會經過驗證並儲存到記憶體資料庫中。
為了讓您的應用程式在 iOS 和 Android 上都能運作,您首先需要將部分程式碼移至共享模組(shared module),使您的程式碼具備跨平台能力。之後,您將在 Android 應用程式中使用您的跨平台程式碼,然後在新的 iOS 應用程式中使用相同的程式碼。
如果您不熟悉 Kotlin Multiplatform,請先了解如何從頭開始建立跨平台應用程式。
準備開發環境
在快速入門指南中,完成設定 Kotlin Multiplatform 開發環境的說明。
您需要一台裝有 macOS 的 Mac 才能完成本教學中的某些步驟,例如執行 iOS 應用程式。這是由於 Apple 的限制。
在 Android Studio 中,從版本控制建立一個新專案:
texthttps://github.com/Kotlin/kmp-integration-samplemaster分支包含專案的初始狀態 – 一個簡單的 Android 應用程式。要查看包含 iOS 應用程式和共享模組的最終狀態,請切換至final分支。切換至 Project 檢視:

讓您的程式碼具備跨平台能力
要使您的程式碼具備跨平台能力,您將遵循以下步驟:
決定哪些程式碼要跨平台
決定 Android 應用程式中的哪些程式碼較適合與 iOS 共享,以及哪些程式碼應保持原生。一個簡單的規則是:盡可能共享您想要重複使用的內容。商業邏輯在 Android 和 iOS 上通常是相同的,因此它是重複使用的絕佳候選者。
在您的範例 Android 應用程式中,商業邏輯儲存在 com.jetbrains.simplelogin.androidapp.data 套件中。您未來的 iOS 應用程式將使用相同的邏輯,因此您也應該使其具備跨平台能力。

為跨平台程式碼建立共享模組
用於 iOS 和 Android 的跨平台程式碼將儲存在共享模組中。Android Studio 和 IntelliJ IDEA 都提供了為 Kotlin Multiplatform 建立共享模組的精靈。
建立一個共享模組以連接現有的 Android 應用程式和您未來的 iOS 應用程式:
在 Android Studio 中,從主選單中選取 File | New | New Module。
在範本清單中,選取 Kotlin Multiplatform Shared Module。將模組名稱保留為
shared並輸入套件名稱:textcom.jetbrains.simplelogin.shared點擊 Finish。精靈會建立共享模組,相應地更改建置腳本,並開始 Gradle 同步。
等待同步完成。您將在
shared目錄中看到以下檔案結構:
如果您想更深入了解產出專案的配置,請參閱 Kotlin Multiplatform 專案結構基礎。
將
shared/build.gradle.kts中的kotlin.android {}區塊替換為以下androidLibrary {}區塊,因為shared模組將作為 Android 應用程式的程式庫使用:kotlinimport org.jetbrains.kotlin.gradle.dsl.JvmTarget kotlin { androidLibrary { namespace = "com.jetbrains.simplelogin.shared" compileSdk = libs.versions.android.compileSdk.get().toInt() compilerOptions { jvmTarget = JvmTarget.JVM_11 } androidResources { enable = true } withHostTestBuilder { } withDeviceTestBuilder { sourceSetTreeName = "test" }.configure { instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } } //... }
將程式碼新增至共享模組
現在您已有共享模組,請在 shared/src/commonMain/kotlin/com.jetbrains.simplelogin.shared 目錄中新增一些要共享的通用程式碼:
建立一個帶有以下程式碼的新
Greeting類別:kotlinpackage com.jetbrains.simplelogin.shared class Greeting { private val platform = getPlatform() fun greet(): String { return "Hello, ${platform.name}!" } }將現有檔案中的程式碼替換為以下內容:
在
commonMain/Platform.kt中:kotlinpackage com.jetbrains.simplelogin.shared interface Platform { val name: String } expect fun getPlatform(): Platform在
androidMain/Platform.android.kt中:kotlinpackage com.jetbrains.simplelogin.shared import android.os.Build class AndroidPlatform : Platform { override val name: String = "Android ${Build.VERSION.SDK_INT}" } actual fun getPlatform(): Platform = AndroidPlatform()在
iosMain/Platform.ios.kt中:kotlinpackage com.jetbrains.simplelogin.shared import platform.UIKit.UIDevice class IOSPlatform: Platform { override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion } actual fun getPlatform(): Platform = IOSPlatform()
現在您有一個通用的 getPlatform() 函式,它會傳回一個包含平台名稱屬性的平台特定物件。
在您的 Android 應用程式中新增對共享模組的相依性
要在您的 Android 應用程式中使用跨平台程式碼,請將共享模組連接至該程式碼,將商業邏輯程式碼移至該處,並使此程式碼具備跨平台能力。
在
app/build.gradle.kts檔案中新增對共享模組的相依性:kotlindependencies { // ... implementation(project(":shared")) }按照 IDE 的建議同步 Gradle 檔案,或使用 File | Sync Project with Gradle Files 選單項目。
在
app/src/main/java/目錄中,開啟com.jetbrains.simplelogin.androidapp.ui.login套件下的LoginActivity.kt檔案。為了確保共享模組已成功連接到您的應用程式,透過在
onCreate()方法中新增Log.i()呼叫,將greet()函式的結果寫入記錄中:kotlinoverride fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) Log.i("Login Activity", "Hello from shared module: " + (Greeting().greet())) // ... }遵循 IDE 的建議匯入缺失的類別。
在工具列中,點擊運行配置下拉選單旁邊的偵錯圖示:

在 Logcat 工具視窗中,在記錄中搜尋 "Hello",您將找到來自共享模組的問候語:

使商業邏輯具備跨平台能力
您現在可以將商業邏輯程式碼提取到 Kotlin Multiplatform 共享模組中的 commonMain 原始碼集。這將允許在 Android 和 iOS 上使用該程式碼。
將商業邏輯程式碼
com.jetbrains.simplelogin.androidapp.data從app目錄移動到shared/src/commonMain目錄下的com.jetbrains.simplelogin.shared套件。
當 Android Studio 詢問您想做什麼時,選取移動套件,然後核准重構作業。

忽略所有關於平台相關程式碼的警告,然後點擊 Refactor Anyway。

移除 Android 特有的程式碼,將其替換為跨平台 Kotlin 程式碼,或使用 expect 和 actual 宣告連接至 Android 特有的 API。詳情請參閱以下章節:
將 Android 特有的程式碼替換為跨平台程式碼
為了讓您的程式碼在 Android 和 iOS 上都能良好運作,請盡可能在移動後的
data目錄中將所有 JVM 相依性替換為 Kotlin 相依性。在
LoginDataValidator類別中,將android.utils套件中的Patterns類別替換為與電子郵件驗證模式相符的 Kotlin 正規表示式:kotlin// Before private fun isEmailValid(email: String) = Patterns.EMAIL_ADDRESS.matcher(email).matches()kotlin// After private fun isEmailValid(email: String) = emailRegex.matches(email) companion object { private val emailRegex = ("[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + "\\@" + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + "(" + "\\." + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + ")+").toRegex() }移除
Patterns類別的匯入指示詞:kotlinimport android.util.Patterns在
LoginDataSource類別中,將login()函式中的IOException替換為RuntimeException。IOException在 Kotlin/JVM 之外不可用。```kotlin // Before return Result.Error(IOException("Error logging in", e)) ``` ```kotlin // After return Result.Error(RuntimeException("Error logging in", e)) ```同時移除
IOException的匯入指示詞:kotlinimport java.io.IOException
實作特定平台的 UUID 產生
在
LoginDataSource類別中,fakeUser的通用唯一識別碼 (UUID) 是使用java.util.UUID類別產生的,該類別在 iOS 中不可用。kotlinval fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe")儘管 Kotlin 標準函式庫提供了一個用於產生 UUID 的類別,但讓我們以此案例練習使用特定平台功能。
在共享程式碼中為
randomUUID()函式提供expect宣告,並在對應的原始碼集中為每個平台(Android 和 iOS)提供其actual實作。您可以進一步了解有關連接到平台特定 API 的資訊。將
login()函式中的java.util.UUID.randomUUID()呼叫更改為randomUUID()呼叫,您將為每個平台實作該呼叫:kotlinval fakeUser = LoggedInUser(randomUUID(), "Jane Doe")在
shared/src/commonMain目錄的com.jetbrains.simplelogin.shared套件中建立Utils.kt檔案,並提供expect宣告:kotlinpackage com.jetbrains.simplelogin.shared expect fun randomUUID(): String在
shared/src/androidMain目錄的com.jetbrains.simplelogin.shared套件中建立Utils.android.kt檔案,並提供 Android 中randomUUID()的actual實作:kotlinpackage com.jetbrains.simplelogin.shared import java.util.* actual fun randomUUID() = UUID.randomUUID().toString()在
shared/src/iosMain目錄的com.jetbrains.simplelogin.shared目錄中建立Utils.ios.kt檔案,並提供 iOS 中randomUUID()的actual實作:kotlinpackage com.jetbrains.simplelogin.shared import platform.Foundation.NSUUID actual fun randomUUID(): String = NSUUID().UUIDString()在
shared/src/commonMain目錄的LoginDataSource.kt檔案中匯入randomUUID函式:kotlinimport com.jetbrains.simplelogin.shared.randomUUID
現在,Kotlin 將為 Android 和 iOS 使用特定平台的 UUID 實作。
在 Android 上執行您的跨平台應用程式
執行 app 運行配置,以確保 Android 應用程式的運作與之前相同。

讓您的跨平台應用程式在 iOS 上運作
一旦您使 Android 應用程式具備跨平台能力,您就可以建立一個 iOS 應用程式並在其中重複使用共享的商業邏輯。
在 Xcode 中建立 iOS 專案
在 Xcode 中,點擊 File | New | Project。
在對話方塊中,切換至 iOS 標籤:

選取 App 範本,然後點擊 Next。
產品名稱指定為 "simpleLoginIOS",然後點擊 Next。

選擇儲存跨平台應用程式的目錄作為專案位置,例如
kmp-integration-sample。在 Android Studio 中,您將得到以下結構:

為了與跨平台專案的其他頂層目錄保持一致,請關閉 Xcode 並將
simpleLoginIOS目錄重新命名為iosApp。如果在 Xcode 開啟時重新命名資料夾,您將收到警告並可能損壞專案。

設定 iOS 專案以使用 KMP 架構
您可以直接在 iOS 應用程式與 Kotlin Multiplatform 建置的架構(framework)之間設定整合。
此方法的替代方案(SwiftPM 和 CocoaPods)已在 iOS 整合方法概覽中涵蓋。
在 Android Studio 中,右鍵點擊
iosApp/simpleLoginIOS.xcodeproj目錄,選取 Open In | Open In Associated Application 以在 Xcode 中開啟 iOS 專案。在 Xcode 中,點擊 Project 導航器中的專案名稱來開啟 iOS 專案設定。
在左側的 Targets 區段中,選取 simpleLoginIOS,然後點擊 Build Phases 標籤。
點擊 + 圖示並選取 New Run Script Phase。

在執行指令碼欄位中貼上以下指令碼:
bashif [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" exit 0 fi cd "$SRCROOT/.." ./gradlew :shared:embedAndSignAppleFrameworkForXcode停用 Based on dependency analysis 選項。 這可確保 Xcode 在每次組建期間都執行該指令碼,並且不會在每次都發出缺少輸出相依性的警告。

將 Run Script 階段向上移動,置於 Compile Sources 階段之前:

在 Build Settings 標籤上,停用 Build Options 下的 User Script Sandboxing 選項:

如果您有不同於預設
Debug或Release的自訂組建組態,請在 Build Settings 標籤上的 User-Defined 下新增KOTLIN_FRAMEWORK_BUILD_TYPE設定,並將其設定為Debug或Release。在 Info 標籤上,新增一個自訂的
CADisableMinimumFrameDurationOnPhone屬性,並將其設定為YES以在 iOS 上啟用高重新整理率。在 Signing & Capabilities 標籤上,選取您的開發團隊,或者如果您尚未建立,請建立一個。這將允許對 KMP 模組產出的
shared架構進行簽署。在這裡您還應確保 Bundle Identifier 設定為唯一值,否則 Xcode 可能會導致組建失敗。
在 Xcode 中組建專案(主選單中的 Product | Build)。 如果一切配置正確,專案應該組建成功 (您可以放心地忽略 "build phase will be run during every build" 警告)
如果您在停用 User Script Sandboxing 選項之前組建了專案,組建可能會失敗: Gradle 精靈程序(daemon)可能已被沙盒化,需要重新啟動。 在組建專案之前,先在專案目錄(在我們的範例中為
kmp-integration-sample)中執行此指令以停止精靈程序:shell./gradlew --stop
在 Android Studio 中設定 iOS 運行配置
確保 Xcode 設定正確後,返回 Android Studio:
在主選單中選取 File | Sync Project with Gradle Files。Android Studio 會自動產生一個名為 simpleLoginIOS 的運行配置。
Android Studio 自動產生一個名為 simpleLoginIOS 的運行配置,並將
iosApp目錄標記為連結的 Xcode 專案。在運行配置清單中,選取 simpleLoginIOS。 選擇一個 iOS 模擬器,然後點擊 Run 以檢查 iOS 運行配置是否正常運作。

在 iOS 專案中使用共享模組
shared/build.gradle.kts 檔案為每個 iOS 目標將 binaries.framework.baseName 屬性定義為 sharedKit。 這是 Kotlin Multiplatform 為 iOS 應用程式組建以供取用的架構名稱。
要測試整合,請在 Swift 程式碼中新增對通用程式碼的呼叫:
在 Android Studio 中,開啟
iosApp/simpleloginIOS/ContentView.swift檔案並匯入架構:swiftimport sharedKit為了檢查它是否已正確連接,請更新
ContentView結構的程式碼,以使用來自shared模組的greet()函式:swiftstruct ContentView: View { var body: some View { Text(Greeting().greet()) .padding() } }使用 Android Studio 的 iOS 運行配置執行應用程式以查看結果:

再次更新
ContentView.swift檔案中的程式碼,以使用共享模組中的商業邏輯來渲染應用程式 UI:kotlin在
simpleLoginIOSApp.swift檔案中,匯入sharedKit模組並指定ContentView()函式的引數:swiftimport SwiftUI import sharedKit @main struct SimpleLoginIOSApp: App { var body: some Scene { WindowGroup { ContentView(viewModel: .init(loginRepository: LoginRepository(dataSource: LoginDataSource()), loginValidator: LoginDataValidator())) } } }再次執行 iOS 運行配置,確認 iOS 應用程式顯示了登入表單。
輸入 "Jane" 作為使用者名稱,"password" 作為密碼。
由於您之前已設定整合,iOS 應用程式會使用通用程式碼驗證輸入:

享受成果 – 僅需更新一次邏輯
現在您的應用程式已成為跨平台應用程式。您可以在 shared 模組中更新商業邏輯,並在 Android 和 iOS 上都能看到結果。
更改使用者密碼的驗證邏輯:"password" 不應該是一個有效的選項。 為此,請更新
LoginDataValidator類別的checkPassword()函式 (要快速找到它,請按兩次 鍵,貼上類別名稱,然後切換到 Classes 標籤):kotlinpackage com.jetbrains.simplelogin.shared.data class LoginDataValidator { //... fun checkPassword(password: String): Result { return when { password.length < 5 -> Result.Error("Password must be >5 characters") password.lowercase() == "password" -> Result.Error("Password shouldn't be \"password\"") else -> Result.Success } } //... }從 Android Studio 執行 iOS 和 Android 應用程式以查看變更 (當您點擊紅色警告三角形時,會出現 iOS 錯誤訊息):

您可以查閱本教學的最終程式碼。
還有什麼可以共享的?
您已經共享了應用程式的商業邏輯,但您也可以決定共享應用程式的其他層級。 例如,ViewModel 類別程式碼在 Android 和 iOS 應用程式中幾乎相同, 如果您的行動應用程式應該具有相同的展示層(presentation layer),您可以將其共享。
下一步是什麼?
一旦您使 Android 應用程式具備跨平台能力,您可以繼續進行:
您可以使用 Compose Multiplatform 在所有平台建立統一的 UI:
您也可以查看社群資源:
