如何在 iOS 项目中添加两个或多个 kotlin 原生模块

ade*_*190 10 android gradle ios kotlin kotlin-multiplatform

TL;博士;

\n\n

如何在 iOS 项目上添加两个或多个 kotlin 原生模块而不出现duplicate symbols错误?

\n\n

详细问题

\n\n

让我们假设一个多模块 KMP 项目如下,其中存在一个适用于 Android 的本机应用程序和一个适用于 iOS 的本机应用程序以及两个用于保存共享 kotlin 代码的通用模块。

\n\n
.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 android\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 app\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 common\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 moduleA\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 moduleB\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ios\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 app\n
Run Code Online (Sandbox Code Playgroud)\n\n

模块 A 包含数据类 HelloWorld 并且没有模块依赖项:

\n\n
package hello.world.modulea\n\ndata class HelloWorld(\n    val message: String\n)\n
Run Code Online (Sandbox Code Playgroud)\n\n

模块 B 包含 HelloWorld 类的扩展函数,因此它依赖于模块 A:

\n\n
package hello.world.moduleb\n\nimport hello.world.modulea.HelloWorld\n\nfun HelloWorld.egassem() = message.reversed()\n
Run Code Online (Sandbox Code Playgroud)\n\n

模块的 build.gradle 配置为:

\n\n
    \n
  • 模块A
  • \n
\n\n
apply plugin: "org.jetbrains.kotlin.multiplatform"\napply plugin: "org.jetbrains.kotlin.native.cocoapods"\n\n\xe2\x80\xa6\n\nkotlin {\n    targets {\n        jvm("android")\n\n        def iosClosure = {\n            binaries {\n                framework("moduleA")\n            }\n        }\n        if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {\xe2\x80\xa6}\n    }\n\n    cocoapods {\xe2\x80\xa6}\n\n    sourceSets {\n        commonMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"\n        }\n        androidMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"\n        }\n        iosMain.dependencies {\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  • 模块B
  • \n
\n\n
apply plugin: "org.jetbrains.kotlin.multiplatform"\napply plugin: "org.jetbrains.kotlin.native.cocoapods"\n\xe2\x80\xa6\n\nkotlin {\n    targets {\n        jvm("android")\n\n        def iosClosure = {\n            binaries {\n                framework("moduleB")\n            }\n        }\n        if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {\xe2\x80\xa6}\n    }\n\n    cocoapods {\xe2\x80\xa6}\n\n    sourceSets {\n        commonMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"\n            implementation project(":common:moduleA")\n        }\n        androidMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"\n        }\n        iosMain.dependencies {\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

它看起来非常简单,如果我将 android 构建 gradle 依赖项配置如下,它甚至可以在 android 上运行:

\n\n
dependencies {\n    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"\n    implementation project(":common:moduleA")\n    implementation project(":common:moduleB")\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,这似乎不是在 iOS 上组织多模块的正确方法,因为运行我./gradlew podspec会得到BUILD SUCCESSFUL以下 pod 的预期结果:

\n\n
apply plugin: "org.jetbrains.kotlin.multiplatform"\napply plugin: "org.jetbrains.kotlin.native.cocoapods"\n\n\xe2\x80\xa6\n\nkotlin {\n    targets {\n        jvm("android")\n\n        def iosClosure = {\n            binaries {\n                framework("moduleA")\n            }\n        }\n        if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {\xe2\x80\xa6}\n    }\n\n    cocoapods {\xe2\x80\xa6}\n\n    sourceSets {\n        commonMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"\n        }\n        androidMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"\n        }\n        iosMain.dependencies {\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

即使运行 a,一旦 Xcode 在 Pods 部分显示模块 A 和模块 B,pod install我就会得到一个看起来正确的成功输出。Pod installation complete! There are 2 dependencies from the Podfile and 2 total pods installed.

\n\n

但是,如果我尝试构建 iOS 项目,则会收到以下错误:

\n\n
apply plugin: "org.jetbrains.kotlin.multiplatform"\napply plugin: "org.jetbrains.kotlin.native.cocoapods"\n\xe2\x80\xa6\n\nkotlin {\n    targets {\n        jvm("android")\n\n        def iosClosure = {\n            binaries {\n                framework("moduleB")\n            }\n        }\n        if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {\xe2\x80\xa6}\n    }\n\n    cocoapods {\xe2\x80\xa6}\n\n    sourceSets {\n        commonMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"\n            implementation project(":common:moduleA")\n        }\n        androidMain.dependencies {\n            implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"\n        }\n        iosMain.dependencies {\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我对 iOS 的了解并不多,所以在我未经训练的眼中,看起来每个模块都在添加自己的版本,而不是使用一些分辨率策略来共享它。

\n\n

如果我只使用模块 A,则代码可以正常工作并按预期运行,所以我知道代码本身是正确的,问题是如何管理超过 1 个模块,因此问题是,如何添加两者(模块 A 和模块 B )在 iOS 上并使一切正常运行?

\n\n

聚苯乙烯

\n\n

我确实尽可能地减少了代码,试图只保留我认为是问题根源的部分,但是,如果您想检查片段中缺少的任何内容,或者如果您想要,可以在此处找到完整的代码运行并尝试解决问题\xe2\x80\xa6

\n

Kev*_*vin 5

多个 Kotlin 框架可能很棘手,但从 1.3.70 开始应该可以工作,我看到你已经有了。

问题似乎是两个框架都是静态的,这是目前 1.3.70 中的一个问题,因此它不起作用。(这应该在 1.40 之前更新)。看起来默认情况下 cocoapods 插件将框架设置为静态,这是行不通的。我不知道如何更改 cocoapods 以将其设置为动态,但我已经测试过在没有 cocoapods 的情况下进行构建并isStatic在 gradle 任务中使用该变量,并且已经获得了一个要编译的 iOS 项目。就像是:

binaries {
    framework("moduleA"){
        isStatic = false
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用上面的代码并创建一个任务来构建框架,从而使用此方法解决该问题(这是一个示例

另一件值得注意的事情是,在 iOS 端,HelloWorld 类将显示为两个单独的类,尽管它们都来自 moduleA。对于多个 Kotlin 框架来说,这是另一种奇怪的情况,但我认为该扩展在这种情况下仍然可以工作,因为您返回的是一个字符串。

实际上,我刚刚写了一篇关于多个 Kotlin 框架的博客文章,如果您想看一下,它可能有助于解决其他一些问题。https://touchlab.co/multiple-kotlin-frameworks-in-application/

编辑:看起来cocoapodsext还有一个isStatic变量,所以将其设置为isStatic = false

tl:dr 目前,同一个 iOS 项目中不能有多个静态 Kotlin 框架。使用 将它们设置为非静态isStatic = false