Как добавить два или более собственных модулей kotlin в проект iOS - PullRequest
1 голос
/ 28 апреля 2020

TL; DR;

Как добавить два или более kotlin собственных модуля в iOS проект без получения ошибки duplicate symbols?

Подробный вопрос

Давайте рассмотрим многомодульный проект 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()

Конфигурация модулей build.gradle:

  • Модуль 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, если я сконфигурирую android зависимости gradle сборки следующим образом:

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 install я получаю вывод об успешном выполнении Pod installation complete! There are 2 dependencies from the Podfile and 2 total pods installed., что выглядит правильно, когда Xcode показывает модуль 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 и модуль B) на iOS и сделать все работает?

PS

Я сделал уменьшите код настолько, насколько я мог, стараясь сохранить только те части, которые, как мне кажется, являются источником проблемы, однако полный код доступен здесь , если вы хотите проверить что-либо пропущенное во фрагментах, или если ты Нет, чтобы запустить и попытаться решить проблему ...

1 Ответ

1 голос
/ 28 апреля 2020

Несколько Kotlin фреймворков могут быть хитрыми, но они должны работать с 1.3.70, что, как я вижу, у вас.

Проблема заключается в том, что обе фреймворки устарели c, что в настоящее время проблема в 1.3.70, поэтому она не работает. (Это должно быть обновлено до 1,40). Похоже, что по умолчанию плагин cocoapods устанавливает рамки на stati c, которые не будут работать. Я не знаю, как изменить cocoapods, чтобы установить его как dynamici c, но я протестировал сборку без cocoapods и использовал переменную isStatic в задаче gradle, и получил проект iOS для компиляции. Что-то вроде:

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

На данный момент вы можете обойти проблему, используя этот метод, используя приведенный выше код и создав задачу для построения каркасов (, вот пример )

Еще одна вещь, на которую стоит обратить внимание, это то, что на стороне iOS классы HelloWorld будут отображаться как два отдельных класса, несмотря на то, что оба поступают из moduleA. Это еще одна странная ситуация с несколькими Kotlin фреймворками, но я думаю, что расширение все равно будет работать в этом случае, так как вы возвращаете строку.

Я на самом деле только что написал сообщение в блоге о нескольких Kotlin фреймворках это может помочь с некоторыми другими вопросами, если вы хотите взглянуть. https://touchlab.co/multiple-kotlin-frameworks-in-application/

РЕДАКТИРОВАТЬ : похоже, cocoapodsext также имеет переменную isStatic, поэтому установите ее на isStatic = false

tl : dr В настоящее время в одном проекте iOS не может быть более одной платформы c Kotlin. Установите их как не * stati c, используя isStatic = false.

...