Добавление пакетов Swift в качестве зависимостей модулей KMP
Плагин Kotlin Gradle с интеграцией импорта SwiftPM позволяет импортировать API Objective-C из кода Objective-C и Swift с помощью зависимостей SwiftPM, объявленных для целевых платформ Apple.
Для транзитивных зависимостей (проектов, которые зависят от проектов, использующих импорт SwiftPM) плагин Kotlin Gradle автоматически предоставляет необходимый машинный код из зависимостей SwiftPM. Например, при запуске тестов Kotlin/Native или компоновке фреймворка дополнительная настройка не требуется.
Чтобы настроить проект:
Укажите версию плагина Kotlin Multiplatform Gradle
Чтобы попробовать функцию импорта SwiftPM, убедитесь, что используете версию 2.4.20-RC3 плагина Kotlin Multiplatform Gradle. Пример для файла gradle/libs.versions.toml:
[versions]
kotlin = "2.4.20-RC3"
[plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
Добавление и использование зависимостей SwiftPM
Настройка сборки
Конкретные зависимости SwiftPM можно добавить в блок swiftPMDependencies {} файла build.gradle.kts, где объявляются целевые платформы Apple. Например, для Firebase:
kotlin {
iosArm64()
iosSimulatorArm64()
swiftPMDependencies {
// Import FirebaseAnalytics into your Kotlin code
swiftPackage(
url = url("https://github.com/firebase/firebase-ios-sdk.git"),
version = from("12.5.0"),
products = listOf(product("FirebaseAnalytics")),
)
// swift-protobuf is a transitive Firebase dependency,
// so you only need to include it
// if you want to use a specific version
swiftPackage(
url = url("https://github.com/apple/swift-protobuf.git"),
version = exact("1.32.0"),
products = listOf(),
)
}
}
Интеграция SwiftPM основана на импорте модулей Clang. По умолчанию механизм импорта автоматически обнаруживает модули Clang в указанных пакетах Swift и делает все доступные модули доступными для кода Kotlin — аналогично тому, как работает видимость API в Swift и Objective-C.
Чтобы отключить поведение по умолчанию и автоматическое обнаружение модулей, задайте для discoverClangModulesImplicitly значение false. Если обнаружение модулей отключено, при импорте SwiftPM имена продуктов используются в качестве имён модулей Clang.
Чтобы импортировать модули Clang, имена которых отличаются от имён продуктов, используйте параметр importedClangModules, например:
kotlin {
swiftPMDependencies {
// If 'discoverClangModulesImplicitly' was set to 'true',
// the 'importedClangModules' parameter below would be ignored
discoverClangModulesImplicitly = false
// Imported packages, their products, and Clang modules
swiftPackage(
url = url("https://github.com/firebase/firebase-ios-sdk.git"),
version = from("12.5.0"),
products = listOf(
product("FirebaseAnalytics"),
product("FirebaseFirestore")
),
importedClangModules = listOf(
"FirebaseAnalytics",
// Objective-C APIs of FirebaseFirestore are located
// in the 'FirebaseFirestoreInternal' Clang module
"FirebaseFirestoreInternal"
),
)
}
}
Установка ограничений для платформ
Некоторые зависимости SwiftPM могут не компилироваться или не предоставлять корректные API для всех целевых платформ в скрипте сборки. Например, SDK Google Maps в настоящее время поддерживает только целевые платформы iOS.
Если проект предназначен только для iOS, явно объявлять платформы не нужно. Но как только вы добавите другую целевую платформу (например, macOS), для каждой зависимости потребуется указать ограничение платформы.
Чтобы зависимость применялась только к соответствующим компиляциям, укажите нужные целевые платформы в параметре platforms спецификации product:
kotlin {
iosArm64()
iosSimulatorArm64()
macosArm64()
swiftPMDependencies {
swiftPackage(
url = url("https://github.com/googlemaps/ios-maps-sdk.git"),
version = exact("10.3.0"),
products = listOf(
product(
"GoogleMaps",
platforms = setOf(
// The `GoogleMaps` package will be visible
// only to iOS compilations
iOS()
)
)
)
)
}
}
Запуск задачи интеграции SwiftPM
Инструменты импорта SwiftPM создают промежуточный пакет для отслеживания текущего списка зависимостей SwiftPM. При первом добавлении зависимости SwiftPM в проект необходимо связать проект Xcode с созданным пакетом.
Для этого выполните специальную задачу Gradle, запустив следующую команду в каталоге проекта:
XCODEPROJ_PATH='/path/to/project/iosApp/iosApp.xcodeproj' ./gradlew :kotlin-library:integrateLinkagePackage
Команда создаст пакет SwiftPM и внесёт необходимые изменения в проект Xcode. Не забудьте добавить созданный пакет и обновлённый проект Xcode в репозиторий.
После первоначальной интеграции синтетический пакет будет автоматически обновляться при каждом изменении набора зависимостей SwiftPM или их версий.
Использование импортированных API
Импортированные API Objective-C содержатся в пространствах имён, которые начинаются с префикса swiftPMImport и заканчиваются именами проекта и его группы в Gradle.
Например, в скрипте сборки Kotlin имя группы указано следующим образом:
// subproject/build.gradle.kts group = "groupName"
Здесь groupName — имя группы проекта в Gradle, а subproject — имя проекта. Теперь можно импортировать API Firebase в набор исходного кода iosMain этого модуля, например:
// subproject/src/iosMain/kotlin/useFirebaseAnalytics.kt import swiftPMImport.groupName.subproject.FIRAnalytics import swiftPMImport.groupName.subproject.FIRApp
Создаваемые файлы Package.resolved
Чтобы повысить стабильность сборок, зависящих от пакетов Swift, инструменты импорта SwiftPM добавляют механизм блокировки с помощью файлов Package.resolved. Такие файлы создаются для каждого подпроекта при первоначальном разрешении зависимостей пакетов.
По умолчанию эти файлы объединяются в один файл Package.resolved, размещённый в синтетическом пакете в каталоге .swiftpm-locks/default/swiftImport. Затем для сборки проекта используется общий файл блокировки, что гарантирует использование одинаковых версий пакетов Swift всеми подпроектами. Поведение при объединении файлов блокировки можно настроить, сгруппировав подпроекты или исключив их из синхронизации.
Добавьте файл блокировки в репозиторий, чтобы все сборки использовали одинаковые зависимости. Чтобы упростить управление файлами, можно добавить в репозиторий весь каталог .swiftpm-locks. Для синхронизации зависимостей важны только файлы Package.resolved, но наличие всего каталога может ускорить разрешение зависимостей при первой сборке.
Файлы блокировки обновляются автоматически при изменении набора зависимостей SwiftPM или их версий в скриптах сборки. Также можно вручную принудительно обновить файл блокировки.
Настройка объединения версий пакетов Swift
Вместо использования группы default для всех подпроектов можно задать собственные группы, чтобы для каждой из них создавался отдельный файл блокировки Package.resolved.
Поведение объединения управляется параметром packageResolvedSynchronization в блоке swiftDependencies {}:
kotlin {
swiftDependencies {
// When no value is set for `packageResolvedSynchronization`,
// the subproject is assigned a default group identifier
// as if it were set like this:
// packageResolvedSynchronization = identifier("default")
}
}
Чтобы настроить поведение объединения, задайте для каждого подпроекта идентификатор группы, отличный от идентификатора по умолчанию. В следующем примере подпроекты one и two используют один и тот же набор версий пакетов custom, а подпроект three использует набор по умолчанию:
// one/build.gradle.kts
kotlin {
swiftDependencies {
packageResolvedSynchronization = identifier("custom"),
...
}
}
// two/build.gradle.kts
kotlin {
swiftDependencies {
packageResolvedSynchronization = identifier("custom"),
...
}
}
// three/build.gradle.kts
kotlin {
swiftDependencies {
// The default identifier is used, as if the following is set:
// packageResolvedSynchronization = identifier("default")
...
}
}
Чтобы полностью отключить механизм синхронизации для подпроекта, используйте вызов noSynchronization() вместо identifier():
kotlin {
swiftDependencies {
// The Package.resolved file for this subproject
// won't be merged with any other
packageResolvedSynchronization = noSynchronization()
}
}
Для подпроектов с отключённой синхронизацией будет создан собственный файл блокировки Package.resolved в каталоге подпроекта, рядом с файлом build.gradle.kts.
Как и при синхронизации по умолчанию, все файлы Package.resolved для подпроектов с пользовательскими настройками следует добавить в репозиторий.
Принудительное обновление файла блокировки
Чтобы вручную принудительно обновить файл блокировки:
Удалите каталог
buildдля каждого подпроекта, файлы блокировки которого необходимо обновить.-
Удалите существующий файл
Package.resolved:Для подпроектов без отдельной конфигурации синхронизации удалите каталог
.swiftpm-locks/default/.Для подпроектов с пользовательской группой синхронизации найдите и удалите каталог
.swiftpm-locks/<group-name>/.Для подпроектов, в которых задано
noSynchronization(), найдите и удалите файлPackage.resolvedв каталоге подпроекта.
Повторно запустите задачу разрешения зависимостей:
./gradlew :yourModuleName:fetchSyntheticImportProjectPackages.
Дополнительные параметры импорта
Импорт локальных пакетов Swift
Механизм импорта SwiftPM также позволяет импортировать пакеты Swift из локальной файловой системы.
Рассмотрим пакет Swift со следующим манифестом, расположенным в каталоге /path/to/ExamplePackage:
Чтобы импортировать его в скрипт сборки Kotlin, используйте API localSwiftPackage:
// <projectDir>/shared/build.gradle.kts
kotlin {
swiftPMDependencies {
localSwiftPackage(
directory = project.layout.projectDirectory.dir("/path/to/ExamplePackage/"),
products = listOf("ExamplePackage")
)
}
}
Синхронизируйте файлы Gradle, чтобы выполнить импорт SwiftPM, а затем используйте импортированные API в коде Kotlin:
// /path/to/shared/src/appleMain/kotlin/useExamplePackage.kt
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
fun useExamplePackage() {
// If the Swift package is successfully imported,
// the IDE suggests the correct import for the class
HelloFromExamplePackage().hello()
}
Конкретные версии развёртывания
Если для ваших зависимостей требуется более высокая версия развёртывания, укажите её в параметре *MinimumDeploymentTarget. Например, для iOS:
kotlin {
swiftPMDependencies {
iosMinimumDeploymentTarget.set("16.0")
}
}
Расположение и версия пакетов Swift
Как и в файлах манифеста Package.swift, расположение и версию пакета Swift можно указать в вызове swiftPackage(). Для каждого из них предусмотрено несколько взаимоисключающих параметров.
Чтобы указать расположение, можно использовать URL или идентификатор реестра SwiftPM:
swiftPackage(
// Option 1, URL string
// Points to the Git repository of the package
url = url("https://github.com/firebase/firebase-ios-sdk.git")
// Option 2, Swift Package Registry ID
// See Apple documentation on using a package registry linked above
repository = id("...")
)
Чтобы указать версию, используйте следующие спецификации версий в стиле Gradle и Git:
swiftPackage(
// Similar to the Gradle 'require' version constraint,
// starting with the specified version
version = from("1.0")
// Similar to the Gradle 'strict' version constraint,
// exactly matching the specified version
version = exact("2.0")
// Git-specific version specification,
// matching the specified branch or revision
version = branch("master")
// Or
version = revision("e74b07278b926c9ec6f9643455ea00d1ce04a021")
)
Известные ограничения динамических фреймворков Kotlin/Native
В настоящее время интеграция импорта SwiftPM не поддерживает все особые случаи, которые могут возникнуть при создании динамического фреймворка Kotlin/Native. Во время сборки в Xcode могут возникнуть проблемы или во время выполнения могут появиться предупреждения, например:
Undefined symbols for architecture ...: "...", referenced from: ld: symbol(s) not found ...dyld: Symbol not found: ...objc[...]: Class _Foo is implemented in both /path/to/Shared and /path/to/Bar. This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
Обычно эти проблемы можно устранить, изменив режим связывания фреймворка, задав для свойства isStatic значение true:
// shared/build.gradle.kts
kotlin {
listOf(
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "Shared"
// Set this property to "true"
isStatic = true
}
}
}
Если вы столкнулись с одной из этих проблем, сохраните isStatic=false. Если изменение этого свойства не помогло устранить ошибки сборки, сообщите нам об этом в канале Slack. Получите приглашение и присоединитесь к каналу #kmp-swift-package-manager.
Что дальше?
Подробнее о том, как перейти с CocoaPods на зависимости SwiftPM в проекте KMP.
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform/multiplatform-spm-import.html