Kotlin Multiplatform Library Fundamentals
This skill provides a structured workflow for creating high-quality Kotlin Multiplatform (KMP) libraries. It acts as a Staff Software Architect, ensuring best practices for API design, build configuration, and platform compatibility are followed from the start.
The primary goal is to produce modular, maintainable libraries that seamlessly integrate with legacy JVM (Java 8) ecosystems, as well as modern native (iOS) and JavaScript targets.
Workflow: Creating a KMP Library
Follow these sequential steps to ensure a consistent and robust library setup.
1. Initialize the Module Structure
A clean, conventional directory structure is the foundation of a KMP library. This skill provides a script to automate this process.
- •
Execute the Scaffolding Script: Run the
init_gradle_module.shscript to create the standard source sets (commonMain,jvmMain,iosMain,jsMain) and a pre-configuredbuild.gradle.ktsfile.bash/bin/bash ./scripts/init_gradle_module.sh <your-module-name>
- •
Review the Generated Files: Inspect the created directory structure and the initial
build.gradle.kts. It includes essential configurations covered in the next step.
2. Configure the Gradle Build (build.gradle.kts)
Proper Gradle configuration is critical for compatibility and API stability.
- •
Set JVM Compatibility: Ensure the
jvmToolchainis set to 8. This guarantees that the generated bytecode is compatible with legacy Java 8 runtimes.kotlinkotlin { jvm { withJava() jvmToolchain(8) } // ... other targets } - •
Enforce Explicit API Mode: All KMP libraries must use explicit API mode to prevent unintentionally exposing internal APIs. This makes the public API surface deliberate and clear.
kotlinkotlin { explicitApi() // Or explicitApiWarning() during initial development } - •
Define Targets: Configure the required targets (e.g.,
jvm,iosX64,iosArm64,iosSimulatorArm64,js).
3. Design the Shared API (commonMain)
The commonMain source set contains the core logic and public API of your library.
- •
Prioritize Dependency Injection: Avoid overusing
expect/actual. For platform-specific implementations, prefer passing interfaces into yourcommonMaincode from the platform-specific source sets. For a detailed guide, readreferences/kmp_architecture.md. - •
Embrace Immutability: Design your data classes and state holders to be immutable (
valovervar). This is crucial for preventing concurrency issues, especially on native platforms like iOS. - •
Expose Suspending Functions for Asynchrony: Use
suspendfunctions for asynchronous operations. This pattern translates well across platforms and coroutines runtimes.
4. Manage Dependencies
- •Use
commonMainfor Shared Dependencies: Declare dependencies that are published for all targets in thecommonMainsource set.kotlinsourceSets { val commonMain by getting { dependencies { api("io.insert-koin:koin-core:x.y.z") // Example: DI framework } } } - •Use Platform-Specific Source Sets for Platform Dependencies: For dependencies that are specific to a platform (e.g., OkHttp for JVM), declare them in the corresponding source set (
jvmMain,nativeMain, etc.).
5. Address iOS Interoperability
When exposing your API to Swift/Objective-C, certain Kotlin features require careful handling. Review references/ios_interop_guidelines.md for a complete list of pitfalls and solutions.