Integrating Swift Packages in Kotlin Multiplatform with Kotlin 2.4.x
Leveraging built-in SwiftPM import to streamline native iOS dependency management in KMP projects.
1The Challenge of Native SDKs
Kotlin Multiplatform simplifies cross-platform logic, but integrating platform-specific libraries like Google Nearby Connections remains complex. While Swift packages compile, their APIs are not automatically accessible to Kotlin. Developers often face issues where generated bindings remain empty, requiring manual intervention to bridge Swift code with Kotlin/Native's Objective-C interoperability requirements.
2Limitations of Swift Interoperability
Kotlin/Native relies heavily on Objective-C for interoperability, meaning it cannot directly expose arbitrary Swift APIs. Even if a package compiles, the generated Kotlin bindings may lack useful declarations. Developers must often write local Swift wrappers using @objc and @objcMembers to ensure that native SDK methods become visible to Kotlin.
3Transitioning to Built-in SwiftPM
Kotlin 2.4.0 introduced native SwiftPM import, replacing the need for third-party plugins like spm4Kmp. This migration involves creating a local Swift package that acts as a wrapper for the target SDK. By defining a Package.swift manifest, developers gain better control over dependency resolution and the specific modules exposed to Kotlin.
4Configuring the Gradle Build
In the new setup, the Gradle configuration points directly to the local Swift package. Developers must specify the SwiftPM product and the Clang module to import. Setting discoverClangModulesImplicitly to false is recommended to ensure explicit module selection, preventing the build system from automatically discovering and potentially misconfiguring available native modules.
5Xcode Integration and Linkage
Beyond Gradle, the iOS application requires native code linkage. Kotlin tooling generates an intermediary package called KotlinMultiplatformLinkedPackage, which connects Swift dependencies to the Xcode project. The :integrateLinkagePackage task automates this process, updating the project and ensuring that transitive dependencies are correctly resolved and locked within the version control system.
6Addressing Transitive Module Errors
During the migration from Kotlin 2.3.20 to 2.4.0, developers might encounter fatal errors regarding missing modules like NearbyCoreAdapter. This occurs when the generated Swift header references a transitive dependency not included in the cinterop configuration. While manual compilerOpts workarounds exist, the built-in SwiftPM import provides a cleaner, more robust path forward.
7CI and Project Configuration
Successful migration requires minor adjustments to the CI environment, specifically setting the rootProject.name explicitly in settings.gradle.kts. Additionally, the import paths in Kotlin code change from the old plugin style to the new swiftPMImport.projectName.moduleName structure. These small adjustments ensure consistent builds across different development environments and automated integration pipelines.
8Evaluating the Migration Effort
Migrating to the built-in SwiftPM import is worthwhile if you face unresolved integration issues or seek to simplify build configurations. However, since the feature is still in alpha, it is not a universal fix for every Swift package. If an existing integration is stable, maintaining the current setup is often safer.