作者简介
Derek,携程资深研发经理,关注Native技术、跨平台领域。
前言
KMM(Kotlin Multiplatform Mobile),2022年10月迎来了KMM的beta版,携程机票也是从KMM开始出道的alpha版本就已在探索。
本文主要围绕下面几个方面展开说明:
如何在KMM项目中配置iOS的依赖
KMM工程的CI/CD环境搭建和配置
常见的集成问题的解决方法
本文适合于对KMM有一定的了解的iOS开发者,KMM相关资料可参阅Kotlin Multiplatform官网介绍。
一、背景
携程App已有很长的历史了,在类似这样一个庞大成熟的App中要引入一套新的跨端框架,最先考虑的就是接入成本。而历史的跨端框架以及现存的RN、Flutter等,都需要大量的基建工作,最后才能利用上这个跨平台框架。
通常对于大型的APP引用新的框架,通信本身的属性肯定是没问题的,那么最关键要解决的就是对现有依赖的处理,像RN和Flutter如果需要对iOS原生API调用,需要从RN和Flutter内部底层增加访问API,而对于现有成型的一些API或者第三方SDK的API调用,将需要在iOS的工程中写好对接的接口API才可以实现,而这个工作量是巨大的。而KMM这个跨端框架,正好可以规避这个问题,他只需要通过简单的配置就可直接调用原有的API,甚至不需要写额外的路由代码就可以实现。
二、如何在KMM项目中配置iOS的依赖
针对不同的开发阶段,工程的依赖环境也是不一样的,大致可以分为下面几种情况:
2.1 只依赖系统框架(项目刚起步、开发完全独立的框架)
按照官方的介绍,直接进行逻辑开发,依赖于iOS平台相关的,在引用API时,只需 import platform.xxx即可,更多内容可参见官方文档。如:
import platform.UIKit.UIDevice
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
2.2 有部分API的依赖(一定的代码积累,但又不想在KMM中重写已有的API)
此种情况KMM可以直接依赖原始逻辑,只需要将依赖的文件声明,做成一个def文件,通过官方提供的cinterop工具将其转换为KMM内部能调用的API即可。
这里官网是在C interop中介绍的,而这其实也可以直接用到Objective-C中。
方法如下:xxx.def
language = Objective-C
headers = AAA.h BBB.h
compilerOpts = -I/xxx(/xxx为h文件所在目录)
另外需要将def文件位置告知KMM工程,同时设置包名,具体如下:
compilations["main"].cinterops.create(name) {
defFile = project.file("src/nativeInterop/cinterop/xxx.def")
packageName = "com.xxx.ioscall"
}
最终,在KMM调用时,只需要按照正常的kotlin语法调用。(这里能正常import的前提是需要保证def能正常通过cinterop转换为klib,并会被添加到KMM项目中的External Libraries中)
import com.xxx.ioscall.AAA
携程机票最开始的做法也是这种方式,同时为了应对API的变更同步,将iOS工程作为KMM的git submodule,这样def的配置中就可以引用相对路径下的头文件,同时也避免了不同的开发人员源文件路径不同导致的寻址错误问题。
这里注意KMM项目中实际无法真实调用,只是做了编译检查,真实调用需要到iOS平台上才可以。
2.3 依赖本地现有/第三方的framework/library
此种情况方法和上述类似,同样需要依赖创建一个def,但需要添加一些对framework/library的link配置才可以。有了2中的方式后,还需要增加静态库的依赖配置项staticLibraries,如下:
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
由于业务的逐渐增多,我们对基础API也依赖的多了,因而此部分API也是在封装好的Framework/Library中,故我们第二阶段也增加诸如上面对静态库的配置。(这里同样需要注意配置的路径,最好是相对路径)
2.4 依赖私有/公用的pods,携程机票也在开发过程中遇到了基础部门对iOS工程Cocoapods集成改造,现在也是用此种方式进行的依赖集成。
这种方式在iOS中是比较成熟的,也是比较方便的,但也是我们在集成时遇到问题较多的,特别是自定义的pods仓库,而我们项目中依赖的pods比较复杂多样,涵盖了源码、framework,library,swift多种依赖。
如官网上提及的AFNetworing,其实很简单就可以添加到KMM中,但是用到自建的pods仓库时,就会遇到一些问题。这里基础步骤和官网一致,需要对cocoapods中的specRepos、pod等进行配置。如果是私有pods库,并有依赖静态库,具体集成步骤如下:
1)添加cocoapods的相关配置,如下:
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&#