环境
MacOS 15.6、Xcode 16.4、IDEA 2025.2、Gradle 8.14.3、Kotlin 2.2.0
步骤
创建项目
-
通过IDEA的
Kotlin Multiplatform插件创建项目仅选择 iOS,不共享 UI
-
添加依赖
-
在
libs.versions.toml添加如下依赖[versions] junit = "4.13.2" kotlin = "2.2.0" kotlinSerialization = "1.9.0" kotlinCoroutines = "1.10.1" kotlinDateTime = "0.6.2" ktor = "3.2.3" [libraries] junit = { module = "junit:junit", version.ref = "junit" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinCoroutines" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinDateTime" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } ktor-client-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" } ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } [plugins] kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinSerialization" } ktor = { id = "io.ktor.plugin", version.ref = "ktor" } kotlinCocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" } -
在根模块的
build.gradle.kts里添加插件plugins { alias(libs.plugins.kotlinMultiplatform) apply false alias(libs.plugins.kotlinSerialization) apply false alias(libs.plugins.ktor) apply false } -
在
shared或是composeApp模块的build.gradle.kts文件内设置 source set 对应的依赖,以及编译目标plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.kotlinSerialization) alias(libs.plugins.ktor) } kotlin { iosX64() iosArm64() iosSimulatorArm64() sourceSets { commonMain.dependencies { implementation(libs.kotlinx.serialization.json) implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.datetime) implementation(libs.ktor.client.core) implementation(libs.ktor.client.content.negotiation) implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.ktor.client.auth) implementation(libs.ktor.client.logging) } commonTest.dependencies { implementation(libs.kotlin.test) implementation(libs.junit) } iosMain.dependencies { implementation(libs.ktor.client.darwin) } } }
-
-
添加
Kotlin Cocoapods插件-
在根项目的
build.gradle.kts里添加插件plugins { alias(libs.plugins.kotlinMultiplatform) apply false alias(libs.plugins.kotlinSerialization) apply false alias(libs.plugins.ktor) apply false alias(libs.plugins.kotlinCocoapods) apply false } -
在
shared模块的build.gradle.kts里添加插件并配置 cocoapodsimport org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType import kotlin.collections.set plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.kotlinSerialization) alias(libs.plugins.ktor) alias(libs.plugins.kotlinCocoapods) } kotlin { cocoapods { summary = "An API Client for xxx-iOS Target" homepage = "https://github.com/ShiinaKin/xxxAPIClientLib" name = "xxxAPIClient" authors = "ShiinaKin" version = "0.1.0" podfile = rootProject.file("../xxxAPIClientTestDemo/Podfile") source = "{ :git => '$homepage', :tag => spec.version.to_s() }" framework { // 这里的值就是在Swift里import的库名 baseName = "xxxAPIClient" } specRepos { // 这个 URL 只是为了触发正确的行为,可以任意合法 git 仓库 url("https://github.com/ShiinaKin/xxxAPIClientLib") } // Maps custom Xcode configuration to NativeBuildType xcodeConfigurationToNativeBuildType["CUSTOM_DEBUG"] = NativeBuildType.DEBUG xcodeConfigurationToNativeBuildType["CUSTOM_RELEASE"] = NativeBuildType.RELEASE } iosX64() iosArm64() iosSimulatorArm64() sourceSets { commonMain.dependencies { implementation(libs.kotlinx.serialization.json) implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.datetime) implementation(libs.ktor.client.core) implementation(libs.ktor.client.content.negotiation) implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.ktor.client.auth) implementation(libs.ktor.client.logging) } commonTest.dependencies { implementation(libs.kotlin.test) implementation(libs.junit) } iosMain.dependencies { implementation(libs.ktor.client.darwin) } } }
-
-
运行
:shared:podInstall任务可以看到在
shared模块下生成了xxxAPIClient.podspec文件该文件里的
spec.source为空是正常的,该文件只是为了调试使用
本地测试
-
在KMP项目的同级目录下创建一个Swift项目
-
引入依赖
platform :ios target 'xxxAPIClientTestDemo' do use_frameworks! pod 'xxxAPIClient', :path => '../apiClient/shared' end指向
xxxAPIClient.podspec所在的模块我的KMP项目的
settings.gradle.kts的rootProject.name为apiclient -
本地测试是在 XCode 构建时才会编译生成引入的库文件,具体的行为可以看
xxxAPIClient.podspec里的脚本,因此在build里只有一个placeholder.h是正常的XCode 构建时找不到 JavaRuntime
我是通过 SDKMan 管理 JDK 的,因此 XCode 会在构建时报这个错
解决方案:
- 创建文件
~/Library/LaunchAgents/sdkman-java-home.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.sdkman.set-java-home</string> <key>ProgramArguments</key> <array> <string>/usr/bin/true</string> </array> <key>RunAtLoad</key> <true/> <key>EnvironmentVariables</key> <dict> <key>JAVA_HOME</key> <string>/Users/shiinakin/.sdkman/candidates/java/current</string> </dict> </dict> </plist>- 添加为启动项:
launchctl load ~/Library/LaunchAgents/sdkman-java-home.plist - 重启XCode
- 执行
launchctl getenv JAVA_HOME看看是否能拿到 - 重新构建
- 创建文件
-
编写业务代码
在Swift文件里引入库
import xxxAPIClient
发布CocoaPods包
运行:shared:podPublishXCFramework任务
在 shared/build/cocoapods/publish 下找到产物
CocoaSpec仓库
以 https://github.com/ShiinaKin/xxxAPIClientPodSpec 为例
将产物的 podspec 文件提交到这个仓库,文件结构如下
.
└── Specs
└── xxxAPIClient
└── 0.1.0
└── xxxAPIClient.podspec
└── 0.1.1
└── xxxAPIClient.podspec
这个仓库的提交无需打 tag
XCFramework仓库
以 https://github.com/ShiinaKin/xxxAPIClientLib 为例,这个地址应该与产物的 podspec 里 source 的地址一致
将产物的 xxx.xcframework 提交到这个仓库,文件结构如下
.
└── xxxAPIClient.xcframework
每次提交后,对该提交打一个 tag , tag 为 podspec 里对应的版本,例如 0.1.0
每次发布新的版本时,首先将旧的文件移除,然后将新的产物移动过来,然后提交,发布tag
使用CocoaPods包
source 'https://github.com/ShiinaKin/xxxAPIClientPodSpec'
source 'https://cdn.cocoapods.org/'
platform :ios, '15.0'
target 'xxx' do
use_frameworks!
pod 'SVProgressHUD', '~> 2.3.1'
# other dependencies
pod 'xxxAPIClient', '~> 0.1.0'
end
参考: