提问者:小点点

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


如何添加两个或更多的kotlin原生模块在一个iOS项目没有得到重复的符号错误?

让我们假设一个多模块KMP项目作为后续,其中存在一个用于Android的本地应用程序和一个用于iOS的本地应用程序,以及两个用于保存共享kotlin代码的公共模块。

.
├── android
│   └── app
├── common
│   ├── moduleA
│   └── moduleB
├── ios
│   └── app

模块A包含一个数据类HelloWorld,没有模块依赖关系:

package hello.world.modulea

data class HelloWorld(
    val message: String
)

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

package hello.world.moduleb

import hello.world.modulea.HelloWorld

fun HelloWorld.egassem() = message.reversed()

建造。模块的渐变配置为:

  • 模块A
apply plugin: "org.jetbrains.kotlin.multiplatform"
apply plugin: "org.jetbrains.kotlin.native.cocoapods"

…

kotlin {
    targets {
        jvm("android")

        def iosClosure = {
            binaries {
                framework("moduleA")
            }
        }
        if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {…}
    }

    cocoapods {…}

    sourceSets {
        commonMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"
        }
        androidMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"
        }
        iosMain.dependencies {
        }
    }
}
  • 模块B
apply plugin: "org.jetbrains.kotlin.multiplatform"
apply plugin: "org.jetbrains.kotlin.native.cocoapods"
…

kotlin {
    targets {
        jvm("android")

        def iosClosure = {
            binaries {
                framework("moduleB")
            }
        }
        if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {…}
    }

    cocoapods {…}

    sourceSets {
        commonMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"
            implementation project(":common:moduleA")
        }
        androidMain.dependencies {
            implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"
        }
        iosMain.dependencies {
        }
    }
}

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

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"
    implementation project(":common:moduleA")
    implementation project(":common:moduleB")
}

然而,这似乎不是在iOS上组织多模块的正确方法,因为运行./gradlew podspec我得到了一个BUILD SUCCESSFUL,正如预期的那样:

pod 'moduleA', :path => '…/HelloWorld/common/moduleA'
pod 'moduleB', :path => '…/HelloWorld/common/moduleB'

即使运行pod安装我也能成功地完成pod安装!Podfile中有2个依赖项,总共安装了2个pod一旦Xcode在Pods部分显示模块A和模块B,什么看起来正确。

但是,如果我尝试构建iOS项目,会出现以下错误:

Ld …/Hello_World-…/Build/Products/Debug-iphonesimulator/Hello\ World.app/Hello\ World normal x86_64 (in target 'Hello World' from project 'Hello World')
    cd …/HelloWorld/ios/app
…
duplicate symbol '_ktypew:kotlin.Any' in:
    …/HelloWorld/common/moduleA/build/cocoapods/framework/moduleA.framework/moduleA(result.o)
    …/HelloWorld/common/moduleB/build/cocoapods/framework/moduleB.framework/moduleB(result.o)
… a lot of duplicate symbol more …
duplicate symbol '_kfun:kotlin.throwOnFailure$stdlib@kotlin.Result<#STAR>.()' in:
    …/HelloWorld/common/moduleA/build/cocoapods/framework/moduleA.framework/moduleA(result.o)
    …/HelloWorld/common/moduleB/build/cocoapods/framework/moduleB.framework/moduleB(result.o)
ld: 9928 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

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

如果我只使用模块A,代码可以正常工作和运行,因此我知道代码本身是正确的,问题是如何管理多个模块,因此问题是,如何在iOS上同时添加(模块A和模块B)并使其正常工作?

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


共2个答案

匿名用户

但是,如果我尝试构建iOS项目,会出现以下错误:

此特定错误是已知的问题。多个调试静态框架与编译器缓存不兼容。

因此,要解决这个问题,您可以通过将以下行放入gradle来禁用编译器缓存。属性

kotlin.native.cacheKind=none

或者,通过向Gradle构建脚本中添加以下代码片段,使框架成为动态的:

kotlin {
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework> {
            isStatic = false
        }
    }
}

看见https://youtrack.jetbrains.com/issue/KT-42254更多细节。

我猜当前多框架的行为对于最初的主题初学者来说没有多大意义,我只是在这里为可能遇到相同问题的任何人提供答案。

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

这正是它在此刻应该如何工作。但是每个框架中的“东西的版本”都被放入单独的独立命名空间,所以不应该有链接错误,你遇到的是一个错误。

匿名用户

多个静态编程语言框架可能很棘手,但应该在1.3.70工作,我看到你已经有了。

问题似乎是这两个框架都是静态的,这在1.3中是一个问题。70所以它不起作用了。(应在1.40之前进行更新)。默认情况下,cocoapods插件会将框架设置为静态的,这是不起作用的。我不知道有什么方法可以将cocoapods设置为动态的,但是我已经测试过不使用cocoapods的构建,并在gradle任务中使用isStatic变量,并且已经得到了一个iOS项目来编译。比如:

binaries {
    framework("moduleA"){
        isStatic = false
    }
}

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

另一个值得注意的是,在iOS方面,HelloWorld类将显示为两个独立的类,尽管它们都来自moduleA。对于多个静态编程语言框架来说,这是另一种奇怪的情况,但是我认为扩展在这种情况下仍然可以工作,因为您要返回一个字符串。

事实上,我刚刚写了一篇关于多个静态编程语言框架的博客文章,如果你想看一看,这可能有助于解决其他一些问题。https://touchlab.co/multiple-kotlin-frameworks-in-application/

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

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