Spec-Zone.ru › Kotlin 2

Добавление пакетов Swift в качестве зависимостей модулей KMP

Эта функция находится на этапе Alpha. Пожалуйста, поделитесь проблемами или отзывами в специальном канале Kotlin в Slack: #kmp-swift-package-manager

Плагин Kotlin Gradle с интеграцией импорта SwiftPM позволяет импортировать API Objective-C из кода Objective-C и Swift с помощью зависимостей SwiftPM, объявленных для целевых платформ Apple.

Для транзитивных зависимостей (проектов, которые зависят от проектов, использующих импорт SwiftPM) плагин Kotlin Gradle автоматически предоставляет необходимый машинный код из зависимостей SwiftPM. Например, при запуске тестов Kotlin/Native или компоновке фреймворка дополнительная настройка не требуется.

Экспорт модулей KMP, использующих импорт SwiftPM, в качестве пакета Swift пока не поддерживается и может работать некорректно. Подробнее см. в этой задаче YouTrack и расскажите нам о вашем сценарии использования.

Чтобы настроить проект:

  1. Настройте среду разработки

  2. Добавьте зависимости SwiftPM в модуль KMP и используйте их

Укажите версию плагина 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

Примеры работающих проектов см. в наших примерах. В ветке master каждый проект настроен с использованием CocoaPods, а в ветке spm_import используется SwiftPM:

  • Пример приложения SwiftUI и Firebase

  • Пример приложения Compose Multiplatform для iOS

Настройка сборки

Конкретные зависимости 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 для подпроектов с пользовательскими настройками следует добавить в репозиторий.

Принудительное обновление файла блокировки

Чтобы вручную принудительно обновить файл блокировки:

  1. Удалите каталог build для каждого подпроекта, файлы блокировки которого необходимо обновить.

  2. Удалите существующий файл Package.resolved:

    • Для подпроектов без отдельной конфигурации синхронизации удалите каталог .swiftpm-locks/default/.

    • Для подпроектов с пользовательской группой синхронизации найдите и удалите каталог .swiftpm-locks/<group-name>/.

    • Для подпроектов, в которых задано noSynchronization(), найдите и удалите файл Package.resolved в каталоге подпроекта.

  3. Повторно запустите задачу разрешения зависимостей: ./gradlew :yourModuleName:fetchSyntheticImportProjectPackages.

Дополнительные параметры импорта

Импорт локальных пакетов Swift

Механизм импорта SwiftPM также позволяет импортировать пакеты Swift из локальной файловой системы.

Рассмотрим пакет Swift со следующим манифестом, расположенным в каталоге /path/to/ExamplePackage:

// /path/to/ExamplePackage/Package.swift let package = Package( name: "ExamplePackage", platforms: [.iOS("15.0")], products: [ .library(name: "ExamplePackage", targets: ["ExamplePackage"]), ], dependencies: [ .package(url: "https://github.com/grpc/grpc-swift.git", exact: "1.27.0",), ], targets: [ // This target can be implemented in Swift with @objc API or in Objective-C .target(name: "ExamplePackage", dependencies: [.product(name: "GRPC", package: "grpc-swift")]), ] )

Чтобы импортировать его в скрипт сборки 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.

21 июля 2026 г.
Настройка экспорта пакета SwiftПереход проекта Kotlin Multiplatform с CocoaPods на зависимости SwiftPM

© 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API