Как правильно импортировать многоплатформенный проект Kotlin (и запускать только те задачи, которые необходимы при сборке)

Возможно ли, чтобы все задачи iOS (kotlin / Native) (cinterop + cocoapods) не выполнялись, когда я разрабатываю приложение для Android с импортированным многоплатформенным проектом Kotlin?

Наша текущая структура проекта KMP (SharedLibrary):

      SharedLibrary
 - build.gradle.kts
 - settings.gradle.kts
 - ...
 - shared
   - build.gradle.kts
     - commonMain
     - androidMain (android library target)
     - ...  

У нас есть отдельный проект Android (SomeApp) со следующими настройками для импорта нашей общей библиотеки.

      // settings.gradle.kts
include ":shared"
project(":shared").projectDir = file("../SharedLibrary/shared")

Дело в том, что ... это довольно раздражающий шаблон разработки, особенно потому, что артефакты iOS (cinterop + cocoapods и т. Д.) Перестраиваются каждый раз, когда происходит изменение, даже если меня интересует только артефакт библиотеки Android.

Что я пробовал:

  • Я попытался переключиться на составную сборку, но получаю ошибки, связанные с тем, что gradle не может найти многоплатформенный плагин.
  • Я подумываю перейти на использование локального maven, но это определенно замедлит процесс разработки, поэтому я сопротивляюсь.

Это проблема с нашей настройкой? Или задачи просто зависят друг от друга и выхода нет?

3 ответа

Сразу хочу сказать несколько слов о проблеме с точки зрения болельщика.

Я обнаружил эту проблему YouTrack, которая кажется связанной: https://youtrack.jetbrains.com/issue/KT-43796 . Пожалуйста, проверьте, правильно ли это описывает вашу проблему, и прокомментируйте, если это не так. Кроме того, это лучшее место для получения обновленной информации о статусе проблемы.

Вы можете исключить цель iOS из сборки следующим образом:

      kotlin {
    android()
    // kotlin.native.cocoapods.target property passed by cocoapods build from Xcode 
    val shouldBuildIos = project.findProperty("kotlin.native.cocoapods.target") != null
    if (shouldBuildIos) {
        ios()
        cocoapods {
            // ...
        }
    }
    sourceSets {
        // ...
        if (shouldBuildIos) {
            val iosMain by getting {
                libDependencies(
                    "ktor.client.engine.ios",
                )
            }
        }
    }
}

Но в этом случае у вас не будет предложений кода в IDE. Также будьте осторожны, потому что в этом случае ваш podspec будет удален.

Если вы работаете только над частью Android, а другой разработчик работает с iOS, вы можете создать собственное свойство в local.properties и убедиться, что вы не будете нажимать удаление файла podspec (или просто проигнорируете его)

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

Каждый раз, когда мне это нужно, я создаю интерфейс в общем коде и передаю экземпляр, реализующий его, или функцию «генератор» (если вы не хотите создавать объект, если он не нужен) из части iOS.

в части y iOS это выглядит примерно так:

      featureProvider = FeatureProvider(
    context: .init(
        rootController: rootViewController,
        application: application,
        launchOptions: launchOptions
    ),
    providerGenerator: { socialNetwork, context -> CredentialProvider in
        switch socialNetwork {
        case .facebook:
            return FacebookProvider(context: context)
            
        case .google:
            return GoogleProvider(context: context)
            
        default: fatalError()
        }
    }
)

Если вы думаете об этом, при создании вашего общего модуля gradle на самом деле не знает, кто это будет употреблять, правда?

Я могу придумать два способа решения вашей проблемы:

  • У вас может быть tasks.getByName("build").dependsOn(packForXcode) из официального руководства, вы можете просто удалить это и запустить packForXcode вручную для создания фреймворков iOS
  • Вы можете проверить, есть ли задача по созданию android только цель, если ее нет, вы можете создать ее (просто сгруппируйте соответствующие задачи для Android с помощью dependsOn, затем запустите только это)