The Swift Package Manager for Kotlin multiplatform Plugin aka spmForKmp
gradle plugin is a Gradle plugin designed to simplify integrating Swift Package Manager (SPM) dependencies into Kotlin Multiplatform (KMP) projects. It allows you to (almost) effortlessly configure and use Swift packages in your Kotlin projects targeting Apple platforms, such as iOS.
Also, You can embed your own Swift code for a direct import to your Kotlin library.
This project greatly needs feedback and information about the edge case for progressing; the discussion tab is welcomed.
- Create a bridge easily: Import your own Swift code for functionality can't be done in Kotlin for example.
- Import Swift compatible code to Kotlin: Enable SPM dependencies and your own Swift code to be exposed directly in your Kotlin code (if compatible).
- Automatic CInterop Configuration: Simplify the process of creating native CInterop definitions for your Swift packages with dependencies.
Warning
Pure Swift packages can't be exported to Kotlin; creating a bridge with this plugin is a solution for avoiding this issue.
A fully working sample is available as a playground, and a sample with CMP/Firebase here.
Add the plugin to your build.gradle.kts
or the appropriate Gradle module’s plugins
block:
plugins {
id("org.jetbrains.kotlin.multiplatform")
id("io.github.frankois944.spmForKmp").version("0.0.9") // Apply the spmForKmp plugin
}
In your gradle.properties, you must have this declaration :
kotlin.mpp.enableCInteropCommonization=true
kotlin {
listOf(
iosArm64(), // iOS ARM64 (e.g., for physical devices)
iosSimulatorArm64() // iOS Simulator ARM64 (e.g., for M1/M2 machines)
// and more Apple targets...
).forEach {
it.compilations {
val main by getting {
cinterops.create("nativeExample") // Create a CInterop for `nativeExample`
}
}
}
}
swiftPackageConfig {
create("nativeExample") { // same name as the one in `cinterops.create("...")`
// add your embedded swift code and/or your external dependencies, it's optional
dependency(
SwiftDependency.Package.Remote.Version(
// Repository URL
url = URI("https://github.com/firebase/firebase-ios-sdk.git"),
// Libraries from the package
products = {
// Export to Kotlin for use in shared Kotlin code and use it in your swift code
add("FirebaseCore", "FirebaseAnalytics", exportToKotlin = true)
// Add FirebaseDatabase to your swift code but don't export it
add(ProductName("FirebaseDatabase"))
},
// (Optional) Package name, can be required in some cases
packageName = "firebase-ios-sdk",
// Package version
version = "11.6.0",
),
SwiftDependency.Binary.Local(
path = "$testRessources/DummyFramework.xcframework.zip",
packageName = "DummyFramework",
exportToKotlin = true,
),
SwiftDependency.Package.Local(
path = "$testRessources/LocalSourceDummyFramework",
packageName = "LocalSourceDummyFramework",
products = {
// Export to Kotlin for use in shared Kotlin code, false by default
add("LocalSourceDummyFramework", exportToKotlin = true)
},
),
SwiftDependency.Package.Remote.Version(
url = URI("https://github.com/krzyzanowskim/CryptoSwift.git"),
version = "1.8.1",
products = {
// Can be only used in your "src/swift/nativeExample" code.
add("CryptoSwift")
},
),
// see SwiftDependency class for more use cases
)
}
}
The plugin supports the following configurations :
- Remote Package: A Swift package hosted on a remote repository using version, commit, or branch.
- Local Package: A Swift package located in a local directory.
- Local Binary xcFramework: A xcFramework in a local directory.
- Remote Binary xcFramework: A xcFramework hosted on a remote repository.
For more information, refer to the SwiftDependency file.
You can now add your embedded Swift code in the src/swift/[cinteropname]
folder; it will be your bridge between Swift and Kotlin.
Tip
Your swift code need to be mark as @objc/@objcMembers and the visibility set as public
or it won't be exported and available from your Kotlin code
@objcMembers public class MyOwnSwiftCode: NSObject {
public func exportedMethod() -> String {
return "value"
}
}
The Plug-in is reproducing the CocoaPods plugin behavior with the same kind of issues about third-party dependency but less intrusively.
Important
A local swift package is being generated during the build and this message diplayed
Spm4Kmp: A local Swift package has been generated at
/path/to/the/local/package
Please add it to your xcode project as a local package dependency.
Add the folder to your project as a Local package, that's all.
Note : When updating your configuration, reset the package cache to apply the modification.
You can also use the dependency you have added in the swiftPackageConfig
block in your Swift code.
For example, CryptoSwift
is not a library that can be used directly in Kotlin code, but you can create a bridge in your Swift code.
swiftPackageConfig {
create("dummy") {
dependency(
SwiftDependency.Package.Remote.Version(
url = URI("https://github.com/krzyzanowskim/CryptoSwift.git"),
products = {
add("CryptoSwift")
},
version = "1.8.4",
)
)
}
}
import Foundation
import CryptoSwift
// inside the folder src/swift/dummy
// the class will be automatically accessible from your Kotlin code
@objcMembers public class MySwiftDummyClassWithDependencies: NSObject {
public func toMD5(value: String) -> String {
return value.md5()
}
}
You can also use the dependency you have added in the swiftPackageConfig
in your Kotlin applications.
swiftPackageConfig {
create("dummy") {
dependency(
SwiftDependency.Binary.Local(
path = "path/to/DummyFramework.xcframework.zip",
products = {
add("DummyFramework", exportToKotlin = true)
}
),
)
}
}
You can set a different configuration for each each target you manage.
listOf(
iosX64(),
iosSimulatorArm64(),
).forEach {
it.compilations {
val main by getting {
cinterops.create("nativeIosShared") // a config for iOS
}
}
}
listOf(
macosArm64(),
).forEach {
it.compilations {
val main by getting {
cinterops.create("nativeMacosShared") // a config for macos
}
}
}
swiftPackageConfig {
create("nativeIosShared") {
// your embedded swift is inside the folder src/swift/nativeIosShared
// your config for iOS
}
create("nativeMacosShared") {
// your embedded swift is inside the folder src/swift/nativeMacosShared
// your config for macOS
}
}
This plugin is distributed under the MIT License. For more details, refer to the plugin repository/documentation.
For additional help or detailed documentation, refer to the official repository or contact the plugin maintainers. Happy coding! 🎉