Kotlin Multiplatform Help

Adding Swift packages as dependencies to KMP modules

Kotlin Gradle plugin with SwiftPM import integration allows you to import Objective-C APIs from Objective-C and Swift code using SwiftPM dependencies declared for your Apple targets.

For transitive dependencies (projects that depend on those that use SwiftPM import), the Kotlin Gradle plugin automatically provides the necessary machine code from SwiftPM dependencies. For example, you don't need to do any additional configuration when running Kotlin/Native tests or linking a framework.

To configure your project:

  1. Set up your development environment

  2. Add the SwiftPM dependencies to your KMP module

  3. Use the imported APIs in your Kotlin code

Set up environment

To try out the SwiftPM import functionality, you need to use a specific development version of Kotlin. Keep in mind, this version is not intended for production.

To set up the Kotlin Multiplatform Gradle plugin:

  1. In your settings.gradle.kts file, add the development package repository for dependencies and plugins:

    dependencyResolutionManagement { repositories { maven("https://packages.jetbrains.team/maven/p/kt/dev") mavenCentral() } } pluginManagement { repositories { maven("https://packages.jetbrains.team/maven/p/kt/dev") mavenCentral() gradlePluginPortal() } }
  2. In your version catalog, apply the experimental version of the Kotlin Multiplatform Gradle plugin:

    kotlin = "2.3.20-titan-222" [plugins] kotlin-multiplatform = "2.3.20-titan-222"
  3. Sync Gradle files and try adding a kotlin.swiftPMDependencies {} block to the build.gradle.kts file in your KMP module.

    If the swiftPMDependencies name cannot be resolved, add the following block to the root build.gradle.kts file to force the experimental Kotlin Multiplatform Gradle plugin version:

    buildscript { dependencies.constraints { "classpath"("org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.20-titan-222") } }

Set up KMP IDE plugin

If you are using the Kotlin Multiplatform IDE plugin recommended for KMP projects, explicitly specify the path to the iOS project that is built from the KMP module.

In the build.gradle.kts file where you call the iosTarget.binaries.framework API, add the API call that sets the path:

kotlin { // Example of iOS targets configuration listOf( iosArm64(), iosSimulatorArm64(), iosX64(), ).forEach { iosTarget -> iosTarget.binaries.framework { baseName = "Shared" isStatic = false } } swiftPMDependencies { // Specify the path to the .xcodeproj file that uses // the `:embedAndSignAppleFrameworkForXcode` integration xcodeProjectPathForKmpIJPlugin.set( layout.projectDirectory.file("../iosApp/iosApp.xcodeproj") ) } }

Add and call SwiftPM dependencies

Configure build file

Specific SwiftPM dependencies can be added in the swiftPMDependencies block of the build.gradle.kts file, where your Apple targets are declared. For example, for Firebase:

kotlin { iosArm64() iosSimulatorArm64() iosX64() 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 integration is based on importing Clang modules. By default, the import mechanism automatically discovers Clang modules in specified Swift packages and makes all available modules accessible to Kotlin code — similar to how API visibility works in Swift and Objective-C.

To disable the default behavior and automatic module discovery, set the discoverClangModulesImplicitly to false. When module discovery is disabled, SwiftPM import uses product names as Clang module names.

To import Clang modules whose names differ from product names, use the importedClangModules parameter, for example:

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" ), ) } }

Set platform constraints

Some SwiftPM dependencies may not compile or provide valid APIs for all targets in your build script. For example, the Google Maps SDK currently only supports iOS targets.

So, while your project only targets iOS, you don't need to declare platforms explicitly. But as soon as you add another target, for example, macOS, you need to specify the platform constraint for each dependency.

To make sure that a dependency is applied only for relevant compilations, specify the correct targets in the platforms parameter of a product specification:

kotlin { iosArm64() iosSimulatorArm64() iosX64() 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() ) ) ) ) } }

Use imported APIs

Imported Objective-C APIs are contained in namespaces that start with the swiftPMImport prefix and end with Gradle names of the project and its group.

For example, the Kotlin build script specifies the group name as follows:

// subproject/build.gradle.kts group = "groupName"

Here, groupName is the Gradle group name of the project, and subproject is the project name. Now you can import Firebase APIs in the iosMain source set of that module:

// subproject/src/iosMain/kotlin/useFirebaseAnalytics.kt import swiftPMImport.groupName.subproject.FIRAnalytics import swiftPMImport.groupName.subproject.FIRApp

Additional import options

Importing local Swift packages

The SwiftPM import mechanism also allows importing Swift packages from the local file system.

Let's consider a Swift package with the following manifest, located in the /path/to/ExamplePackage directory:

// /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")]), ] )

To import it in your Kotlin build script, use the localSwiftPackage API:

// <projectDir>/shared/build.gradle.kts kotlin { swiftPMDependencies { localSwiftPackage( directory = project.layout.projectDirectory.dir("/path/to/ExamplePackage/"), products = listOf("ExamplePackage") ) } }

Sync the Gradle files to perform SwiftPM import, then use the imported APIs in your Kotlin code:

// /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() }

Specific deployment versions

If your dependencies require a higher deployment version, specify it in the *MinimumDeploymentTarget parameter. For example, for iOS:

kotlin { swiftPMDependencies { iosMinimumDeploymentTarget.set("16.0") } }

Location and version of Swift packages

Similar to Package.swift manifest files, you can specify the location and version of your Swift package in the swiftPackage() call. Both have a couple of mutually exclusive options.

To set the location, you can use a URL or a SwiftPM registry ID:

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("...") )

To specify the version, use the following Gradle- and Git-style version specifications:

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") )

Known limitations with dynamic Kotlin/Native frameworks

Currently, SwiftPM import integration doesn't support all edge cases that might arise when producing a dynamic Kotlin/Native framework. You might encounter issues during the build in Xcode or see warnings at runtime, for example:

  • 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.

A general fix for these issues is to change the linkage mode of your framework by setting the isStatic property to true:

// shared/build.gradle.kts kotlin { listOf( iosArm64(), iosSimulatorArm64() ).forEach { iosTarget -> iosTarget.binaries.framework { baseName = "Shared" // Set this property to "true" isStatic = true } } }

If you encountered any of these issues, need to keep isStatic=false, or if changing this property didn't help resolve build failures, let us know in our Slack channel. Get an invite and join #kmp-swift-package-manager.

What's next?

Learn more about switching from CocoaPods to SwiftPM dependencies in your KMP project.

12 March 2026