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:
Default 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 android
, iosArm64
, and 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 watchosArm64
, the watchos
source set is created, and the code from the apple
, native
, and 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 targetHierarchy.default()
.
Consider this example of a project with a source set shared between the jvm
and 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.
Target shortcuts
The Kotlin Multiplatform plugin provides some predefined target shortcuts for creating structures for common target combinations:
Target shortcut | Targets |
---|---|
|
|
|
|
|
|
All shortcuts create similar hierarchical structures in the code. For example, you can use theios()
shortcut to create a multiplatform project with 2 iOS-related targets, iosArm64
and iosX64
, and a shared source set:
In this case, the hierarchical structure includes the intermediate source sets iosMain
and 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
The ios
, watchos
, and tvos
target shortcuts don't include the simulator targets for ARM64 (Apple Silicon) platforms: iosSimulatorArm64
, watchosSimulatorArm64
, and 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:
Add the
*SimulatorArm64
simulator target you need.Connect the simulator target with the shortcut using the
dependsOn
relation between source sets.
Manual configuration
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 (linuxX64
, mingwX64
, and macosX64
):

Add the intermediate source set
desktopMain
, which holds the shared logic for these targets.Specify the source set hierarchy using the
dependsOn
relation.
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
Native
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.