skip to content

Intermediate Source Sets

An intermediate source set like iosMain or jvmAndAndroidMain lets a subset of targets share code that is not portable to all of them. It is how you avoid duplicating the same actual across three Apple targets.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

In a Kotlin Multiplatform project, what is an intermediate source set such as iosMain, and why would you create one?

level: juniorimportance: must knowfreq 60%

answer

  1. Shared by SOME targets, not all
  2. Sits between commonMain and leaf sets
  3. iosMain over iosArm64/iosX64
  4. Reaches subset-common APIs (POSIX)
  5. Wired with dependsOn

basics

~20 s

It is a source set shared by some targets but not all. For example iosMain is shared by iosArm64 and iosX64, so you write code once for both iOS targets instead of copying it into each.

solid answer

~40 s

An intermediate source set sits between commonMain (shared by every target) and the leaf target source sets (like iosArm64Main). It groups a subset of targets so they can share code and APIs available only to that subset. A classic example is iosMain, shared by the iosArm64 and iosX64 (and iosSimulatorArm64) targets. You create one to avoid duplicating logic across closely related targets and to reach platform APIs common to that subset (e.g. POSIX on native targets, or java.* across jvm+android). It is wired with dependsOn so the leaf source sets inherit from it, and it can see APIs from all the targets that depend on it. Modern Kotlin can also generate the standard hierarchy automatically via the default hierarchy template.

code

kotlin · 12 lines
kotlin
kotlin {
    applyDefaultHierarchyTemplate()
    iosArm64()
    iosX64()
    iosSimulatorArm64()
    sourceSets {
        // iosMain is auto-created and shared by all three iOS targets
        iosMain.dependencies {
            // dependencies only the iOS family needs
        }
    }
}

go deeper

for a junior

Can define intermediate source set as shared-by-some and give iosMain as an example.

for a middle

Explains it reaches subset-common APIs and is wired via dependsOn / default hierarchy.

for a senior

Discusses the hierarchy tree, visibility rules, and when a custom intermediate set is warranted.

for a principal

Frames source-set hierarchy as an architecture lever for code-sharing strategy across a multi-target codebase.

## What a source set is In Kotlin Multiplatform (KMP) a **source set** is a named bucket of Kotlin source files plus its own dependencies. Each compilation target (JVM, Android, iOS, etc.) gets a *target* source set like `jvmMain` or `iosArm64Main`, and there is a universal `commonMain` whose code compiles for **every** target. ## What an intermediate source set is An **intermediate source set** sits *between* `commonMain` and the per-target leaf source sets. It is shared by **some but not all** targets. Examples: - `iosMain` — shared by `iosArm64`, `iosX64`, `iosSimulatorArm64`. - `nativeMain` — shared by all Kotlin/Native targets. - `jvmAndAndroidMain` — shared by the JVM and Android targets. ## Why create one - **No duplication**: write logic once for a family of related targets instead of copy-pasting into each leaf. - **Reach a subset's common APIs**: code in `iosMain` can call APIs available to *all* iOS targets (e.g. POSIX via `platform.posix`, or Apple Foundation types). Code in `commonMain` cannot, because not every target has them. ## How it is wired Leaf source sets connect to the intermediate set with **`dependsOn`**. With the **default hierarchy template** (enabled by `applyDefaultHierarchyTemplate()` or implied when you only use standard targets), Kotlin creates `iosMain`, `nativeMain`, etc. for you. ```kotlin kotlin { iosArm64() iosX64() // default hierarchy auto-creates iosMain shared by both } ``` ## Mental model Think of a tree: `commonMain` at the root, intermediate sets in the middle, leaf target sets at the bottom. Each node sees only the APIs common to everything below it.

  • Can commonMain call POSIX functions?
    No. POSIX exists only on native targets, not on JVM/JS, so it is not visible from commonMain. It is visible from a native-or-iOS intermediate set.
  • Name another common intermediate set besides iosMain.
    nativeMain (all native targets), appleMain, or a custom jvmAndAndroidMain shared by the JVM and Android targets.

Like a family folder: documents in 'Smith family' apply to all Smiths but not to strangers, while the household-wide folder applies to everyone.

saying these in an interview costs you the question

  • Saying it is shared by all targets (that is commonMain)
  • Claiming commonMain can access platform APIs like POSIX
  • Confusing it with a Gradle module or a build flavor
  • Thinking each target needs its own copy of the shared code

context

open as a page

How does dependsOn create an intermediate source set, and how does it differ from a regular Gradle dependency declaration?

level: middleimportance: must knowfreq 50%

basics

~20 s

dependsOn links one source set to another so it inherits its code and can see its expect declarations. A normal dependency just adds a library on the classpath; dependsOn builds the multiplatform source-set hierarchy itself.

open as a page

What does applyDefaultHierarchyTemplate() do, and when do you still need to define intermediate source sets manually?

level: middleimportance: should knowfreq 45%

basics

~20 s

It auto-creates the standard tree of intermediate source sets (like nativeMain, appleMain, iosMain) and wires them with dependsOn, based on the targets you declared. You only hand-write a set when you need a non-standard grouping the template does not provide.

open as a page

Why can code in iosMain call POSIX or Apple Foundation APIs while commonMain cannot, and how does the hierarchy determine API visibility?

level: seniorimportance: should knowfreq 40%

basics

~20 s

A source set can only use APIs that exist on every target below it. commonMain must compile for all targets, so it sees only the truly universal API. iosMain compiles only for iOS targets, so it can use APIs all iOS targets share, like POSIX or Foundation.

open as a page

Walk through designing a custom jvmAndAndroidMain intermediate source set: when is it worth it, how do you wire it, and what are the pitfalls?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Create it when JVM and Android share a lot of java.*-based code you don't want to duplicate. Wire it with dependsOn so jvmMain and androidMain both inherit from it, and it depends on commonMain. Watch out for Android-only APIs leaking in and template conflicts.

open as a page