Hierarchical project structure
Multiplatform projects support hierarchical structures. This means you can arrange a hierarchy of intermediate source sets for sharing the common code among some, but not all, supported targets. Using intermediate source sets has some important advantages:
If you're a library author, and you want to provide a specialized API, you can use an intermediate source set for some, but not all, targets – for example, an intermediate source set for Kotlin/Native targets but not for Kotlin/JVM ones.
If you want to use platform-dependent libraries in your project, you can use an intermediate source set to use that specific API in several native targets. For example, you can have access to iOS-specific dependencies, such as Foundation, when sharing code across all iOS targets.
Some libraries aren't available for particular platforms. Specifically, native libraries are only available for source sets that compile to Kotlin/Native. Using an intermediate source set will solve this issue.
The Kotlin toolchain ensures that each source set has access only to the API that is available for all targets to which that source set compiles. This prevents cases like using a Windows-specific API and then compiling it to macOS, resulting in linkage errors or undefined behavior at runtime.
There are 3 ways to create a target hierarchy:
Starting with Kotlin 1.8.20, you can set up a source set hierarchy in your multiplatform projects with the default target hierarchy. It's a template for all possible targets and their shared source sets hardcoded in the Kotlin Gradle plugin.
Set up your project
To set up a hierarchy, call
targetHierarchy.default() in the
kotlin block of your
build.gradle(.kts) file and list all of the targets you need. For example:
When you declare the final targets
iosSimulatorArm64 in your code, the Kotlin Gradle plugin finds suitable shared source sets from the template and creates them for you. The resulting hierarchy looks like this:
Green source sets are actually created and present in the project, while gray ones from the default template are ignored. The Kotlin Gradle plugin hasn't created the
watchos source set, for example, because there are no watchOS targets in the project.
If you add a watchOS target, like
watchos source set is created, and the code from the
common source sets is compiled to
watchosArm64 as well.
Adjust the resulting hierarchy
You can further configure the resulting hierarchy manually using the
dependsOn relation. To do so, apply the
by getting construction for the source sets created with
Consider this example of a project with a source set shared between the
native targets only:
It can be cumbersome to remove
dependsOn relations that are automatically created by the
targetHierarchy.default() call. In that case, use an entirely manual configuration instead of calling the default hierarchy.
See the full hierarchy template
When you declare the targets to which your project compiles, the plugin picks the shared source sets based on the specified targets from the template and creates them in your project.
The Kotlin Multiplatform plugin provides some predefined target shortcuts for creating structures for common target combinations:
All shortcuts create similar hierarchical structures in the code. For example, you can use the
ios() shortcut to create a multiplatform project with 2 iOS-related targets,
iosX64, and a shared source set:
In this case, the hierarchical structure includes the intermediate source sets
iosTest, which are used by the platform-specific source sets:
The resulting hierarchical structure will be equivalent to the code below:
Target shortcuts and ARM64 (Apple Silicon) simulators
tvos target shortcuts don't include the simulator targets for ARM64 (Apple Silicon) platforms:
tvosSimulatorArm64. If you use the target shortcuts and want to build the project for an Apple Silicon simulator, make the following adjustment to the build script:
*SimulatorArm64simulator target you need.
Connect the simulator target with the shortcut using the
dependsOnrelation between source sets.
You can manually introduce an intermediate source in the source set structure. It will hold the shared code for several targets.
For example, here’s what to do if you want to share code among native Linux, Windows, and macOS targets (
Add the intermediate source set
desktopMain, which holds the shared logic for these targets.
Specify the source set hierarchy using the
The resulting hierarchical structure will look like this:
You can have a shared source set for the following combinations of targets:
JVM or Android + JS + Native
JVM or Android + Native
JS + Native
JVM or Android + JS
Kotlin doesn't currently support sharing a source set for these combinations:
Several JVM targets
JVM + Android targets
Several JS targets
If you need to access platform-specific APIs from a shared native source set, IntelliJ IDEA will help you detect common declarations that you can use in the shared native code. For other cases, use the Kotlin mechanism of expected and actual declarations.