其他
干货 | 携程机票App KMM iOS工程配置实践
作者简介
Derek,携程资深研发经理,关注Native技术、跨平台领域。
如何在KMM项目中配置iOS的依赖 KMM工程的CI/CD环境搭建和配置 常见的集成问题的解决方法
import platform.UIKit.UIDevice
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
language = Objective-C
headers = AAA.h BBB.h
compilerOpts = -I/xxx(/xxx为h文件所在目录)
compilations["main"].cinterops.create(name) {
defFile = project.file("src/nativeInterop/cinterop/xxx.def")
packageName = "com.xxx.ioscall"
}
import com.xxx.ioscall.AAA
language = Objective-C
package = com.yy.FA
headers = /xxx/TestLocalLibraryCinterop/extframework/FA.framework/Headers/AAA.h
libraryPaths = /xxx/TestLocalLibraryCinterop/extframework/
staticLibraries = FA.framework FB.framework
cocoapods {
summary = "Some description for the Shared Module"
homepage = "https://xxxx.com/xxxx"
version = "1.0"
ios.deploymentTarget = "13.0"
framework {
baseName = "shared"
}
specRepos {
url("https://github.com/hxxyyangyong/yyspec.git")
}
pod("yytestpod"){
version = "0.1.11"
}
useLibraries()
}
gradle.taskGraph.whenReady {
tasks.filter { it.name.startsWith("generateDef") }
.forEach {
tasks.named<org.jetbrains.kotlin.gradle.tasks.DefFileTask>(it.name).configure {
doLast {
val taskSuffix = this.name.replace("generateDef", "", false)
val headers = when (taskSuffix) {
"Yytestpod" -> "TTDemo.h DebugLibrary.h NSString+librarykmm.h TTDemo+kmm.h NSString+kmm.h"
else -> ""
}
val compilerOpts = when (taskSuffix) {
"Yytestpod" -> "compilerOpts = -I${buildDir}/cocoapods/synthetic/IOS/Pods/yytestpod/yytestpod/Classes/DebugFramework.framework/Headers -I${buildDir}/cocoapods/synthetic/IOS/Pods/yytestpod/yytestpod/Classes/library/include/DebugLibrary\n"
else -> ""
}
outputFile.writeText(
"""
language = Objective-C
headers = $headers
$compilerOpts
""".trimIndent()
)
}
}
}
}
``` kotlin
import cocoapods.yytestpod.TTDemo
class IosGreeting {
fun calctTwoDate() {
println("Test1:" + TTDemo.callTTDemoCategoryMethod())
}
}
```
publishing {
repositories {
maven {
credentials {
username = "username"
password = "password"
}
url = uri("http://maven.xxx.com/aaa/yy")
}
}
}
val iosX64Main by getting {
dependencies{
implementation("groupId:artifactId:iosx64-version:cinterop-packagename@klib")
}
}
val iosArm64Main by getting {
dependencies{
implementation("groupId:artifactId:iosarm64-version:cinterop-packagename@klib")
}
}
pre: 主要做一些环境的check操作 build: 执行KMM工程的build test: 执行KMM工程中的UT upload: 上传UT的报告(手动执行) deploy: 发布最终的集成产物(手动执行)
sudo curl --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64
sudo chmod +x /usr/local/bin/gitlab-runner
cd ~
gitlab-runner install
gitlab-runner start
sudo gitlab-runner register --url http://xxx.com/ --registration-token xxx_token
1. Enter tags for the runner (comma-separated):yy-runner
此处需要填写tag,后续设置yaml的tags需要保持一致
2. Enter an executor: instance, kubernetes, docker-ssh, parallels, shell, docker-ssh+machine, docker+machine, custom, docker, ssh, virtualbox:shell
此处我们只需要shell即可
buildKMM:
stage: build
tags:
- yy-runner
script:
- sh ci/createlocalfile.sh
- ./gradlew shared:build
- cp -r -f shared/build/fat-framework/release/ ../tempframework
#!/bin/sh
scriptDir=$(cd "$(dirname "$0")"; pwd)
echo $scriptDir
cd ~
rootpath=$(echo `pwd`)
cd "$scriptDir/.."
touch local.properties
echo "sdk.dir=$rootpath/Library/Android/sdk" > local.properties
stage: test
tags:
- yy-runner
script:
- ./gradlew shared:iosX64Test
- rm -rf ../reporttemp
- mkdir ../reporttemp
- cp -r -f shared/build/reports/ ../reporttemp/${CI_PIPELINE_ID}_${CI_JOB_STARTED_AT}
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
open class SimulatorTestsTask: DefaultTask() {
@InputFile
val testExecutable = project.objects.fileProperty()
@Input
val simulatorId = project.objects.property(String::class.java)
@TaskAction
fun runTests() {
val device = simulatorId.get()
val bootResult = project.exec { commandLine("xcrun", "simctl", "boot", device) }
try {
print(testExecutable.get())
val spawnResult = project.exec { commandLine("xcrun", "simctl", "spawn", device, testExecutable.get()) }
spawnResult.assertNormalExitValue()
} finally {
if (bootResult.exitValue == 0) {
project.exec { commandLine("xcrun", "simctl", "shutdown", device) }
}
}
}
}
```
kotlin{
...
val testBinary = targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosX64").binaries.getTest("DEBUG")
val runIosTests by project.tasks.creating(SimulatorTestsTask::class) {
dependsOn(testBinary.linkTask)
testExecutable.set(testBinary.outputFile)
simulatorId.set(deviceName)
}
tasks["check"].dependsOn(runIosTests)
...
}
val customIosTest by tasks.creating(Sync::class)
group = "custom"
val (deviceName,deviceUDID) = SimulatorHelp.getDeviceNameAndId()
kotlin.targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithSimulatorTests::class.java) {
testRuns["test"].deviceId = deviceUDID
}
val testBinary = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosX64").binaries.getTest("DEBUG")
val runIosTests by project.tasks.creating(SimulatorTestsTask::class) {
dependsOn(testBinary.linkTask)
testExecutable.set(testBinary.outputFile)
simulatorId.set(deviceName)
}
val testBinary = targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosX64").binaries.getTest("DEBUG")
xcrun simctl list runtimes --json
xcrun simctl list devices --json
val (deviceName,deviceUDID) = SimulatorHelp.getDeviceNameAndId()
targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithSimulatorTests::class.java) {
testRuns["test"].deviceId = deviceUDID
}
pages:
stage: build
script:
- yum -y install git
- git status
artifacts:
paths:
- public
only:
refs:
- branches
changes:
- public/index.html
tags:
- official
pods源代码仓库,用于管理每次上传的framework产物,做版本控制。
podspec仓库,管理通过pods源码仓库中的spec的版本
拉取pods源码仓库,替换framework 修改pods源码仓库中的spec文件的version字段 提交修改文件,给pods仓库打上tag,和2中的version一致 将.podspec文件push到spec-repo
linkerOpts("-framework", "XXXFramework","-F${XXXFrameworkPath}")//.framework
linkerOpts("-L${LibraryPath}","-lXXXLibrary")//.a
kotlin {
...
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.all {
linkerOpts("-ObjC")
}
}
...
}
binaries{
getTest(org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG).apply{
linkerOpts("-ObjC")
}
}
getTest(org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG).apply {
linkerOpts("-L/usr/lib/swift")
linkerOpts("-rpath","/usr/lib/swift")
linkerOpts("-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/${platform}")
linkerOpts("-rpath","/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/${platform}")
}
“携程技术”公众号
分享,交流,成长