Kotlin and KMP knowledge map: language contracts, tasks and state, compilation, boundaries, and production judgment.

Level 2 · Fundamentals + best practices

12 illustrated topics for a software engineer learning Kotlin and Android/iOS code sharing.

Scope and evidence

Level 2 · fundamentals and best practices for a software engineer. Twelve topic sheets cover language contracts, collections, models, functions, coroutines, flows, Android/iOS compilation, platform seams, UI sharing, Swift interop, memory, and validation. Official rolling documentation checked on 9 October 2026; current Swift export is Alpha. Code is illustrative and reasoned through, not compiled or executed on Android/iOS. This is a focused mobile introduction, not a complete Kotlin course or ecosystem comparison.

Part 1 · Fundamentals

Nullability: make absence explicit

Prerequisites: basic programming

Branch before dereferencing

Nullable input splits into fallback and non-null handling.
The branches simplify the semantics of safe access; absence is handled before ordinary string operations. [S01]

Purpose synthesis

Read nullable types and decide what absence means at an API boundary.

Mental model verified

String and String? are different contracts. A safe call returns null if its receiver is null; Elvis chooses a fallback. A stable null check can enable a smart cast. Java platform types weaken the compiler’s knowledge, so normalize external data before trusting it.

[S01] [S27]

Worked example synthesis

fun displayName(raw: String?): String {
    val name = raw?.trim()?.takeIf { it.isNotEmpty() }
        ?: return "Guest"
    return name.uppercase()
}
[S01] [S05]

Decision + pitfall synthesis

A guest label fits a display name; it is a poor fallback for an authentication token. Choose return, explicit failure, or fallback from domain semantics. Overusing !! moves the failure back to runtime. Nullable and invalid are separate conditions.

[S01] [S27]

Next improvement synthesis

At a boundary, test null, blank, valid, and malformed values. Keep UI-friendly fallbacks away from security decisions. This is a proposed validation strategy.

[S01] [S22]
Keep this: Treat missing data as a modeled case, not a surprise hidden behind !!.
Check yourself: Does name?.length always produce Int?

Yes: the safe call can produce null even though String.length is non-null.

Part 2 · Fundamentals

val, collections, and ownership

Prerequisites: 01-nullability

Reference versus object

A fixed val reference points to a list that still accepts mutations.
The diagram shows binding rules, not a thread-safety guarantee. [S02]

Purpose synthesis

Separate reference stability, a read-only API, and immutable data.

Mental model verified

val prevents assigning another value to the variable. A MutableList referenced by val still permits add and remove. List offers a read-only interface; it does not guarantee that another alias cannot change the backing collection.

[S02]

Worked example synthesis

val buffer = mutableListOf("Kotlin")
val readView: List<String> = buffer
buffer.add("KMP")
// readView now observes both values
val snapshot = buffer.toList()
// independent list structure; element references are still shared
[S02]

Decision + pitfall synthesis

Keep mutation inside the owner and expose snapshots or read-only state. A shallow snapshot is enough for immutable strings, but not for mutable nested objects. Passing a read-only view between concurrent tasks does not establish synchronization.

[S02] [S03] [S20]

Next improvement synthesis

Test whether a consumer can observe an owner’s later mutation. If snapshots allocate too much, measure first, then consider a persistent immutable representation or a narrower API. These choices trade allocation against ownership clarity.

[S02] [S20]
Keep this: val fixes a binding; immutability is a property of the reachable object graph.
Check yourself: Is val items: List<Item> deeply immutable?

No. The list may have mutable aliases, and Item may itself be mutable.

Part 3 · Fundamentals

Data classes and sealed state models

Prerequisites: 02-ownership

Closed variants and exhaustive rendering

Loading, Ready and Failed are alternative variants consumed by when.
This is a single-request state model. Refreshing previously loaded content requires a richer model. [S03] [S04]

Purpose synthesis

Model payloads and outcomes without inconsistent boolean combinations.

Mental model verified

A data class generates operations such as equals, hashCode and copy from primary-constructor properties. copy is shallow. A sealed hierarchy lets a Kotlin when expression cover the known variants exhaustively; multiplatform expect/actual sealed hierarchies add restrictions.

[S03] [S04]

Worked example synthesis

data class Quote(val id: String, val amountMinor: Long)
sealed interface LoadState {
    data object Loading : LoadState
    data class Ready(val quote: Quote) : LoadState
    data class Failed(val reason: String) : LoadState
}
fun label(s: LoadState): String = when (s) {
    LoadState.Loading -> "Loading"
    is LoadState.Ready -> s.quote.id
    is LoadState.Failed -> s.reason
}
[S03] [S04]

Decision + pitfall synthesis

For this single-request example, variants avoid combinations such as loading=true with error=true. A refreshable screen may legitimately need both existing content and refresh status; model that requirement explicitly. Do not assume a Kotlin data class exports as a Swift struct.

[S04] [S18] [S19]

Next improvement synthesis

Test each transition and keep payloads immutable by convention. Put properties that define identity in the primary constructor, because body properties are excluded from generated equality.

[S03] [S22]
Keep this: Use data classes for payloads and sealed variants for a closed set of outcomes.
Check yourself: Does copy() clone a nested MutableList?

No. Both instances reference that list unless you explicitly replace it.

Part 4 · Fundamentals

Functions, lambdas, and readable transformations

Prerequisites: 03-models

Filter then map

A mixed list becomes an eligible subset and then a list of identifiers.
Arrows describe value transformations. The List example evaluates eagerly. [S05] [S07]

Purpose synthesis

Use behavior as a value while keeping business intent visible.

Mental model verified

A function type such as (Quote) -> Boolean can be passed as a parameter. map transforms elements; extension functions add callable syntax without changing the original class. Extensions are resolved from the declared receiver type, unlike virtual member dispatch.

[S05] [S06] [S07]

Worked example synthesis

fun eligibleIds(
    quotes: List<Quote>,
    allowed: (Quote) -> Boolean
): List<String> = quotes.filter(allowed).map { it.id }

val ids = eligibleIds(quotes) { it.amountMinor > 0 }
// quotes and Quote are caller-provided in this sketch
[S05] [S07]

Decision + pitfall synthesis

Use a lambda for a small local decision; use a named function for a reusable eligibility rule. Long nested lambdas hide control flow. An extension cannot override a real member or gain access to private internals just because it looks like a method.

[S05] [S06]

Next improvement synthesis

The illustrated List pipeline creates intermediate collections. For a hot path, compare it with a loop or lazy sequence using representative input sizes. Do not assume laziness improves a small collection. This is a measurement proposal.

[S02] [S07]
Keep this: Short code earns its keep only when the next reader can see the rule.
Check yourself: Does an extension replace class inheritance?

No. It adds statically resolved syntax, not a new virtual member or state.

Part 5 · Fundamentals

Coroutines: suspension with owned lifetimes

Prerequisites: 04-functions

Task ownership tree

A caller owns a coroutineScope with two async child requests.
Arrows show lifetime ownership. This ordinary coroutineScope fails the combined operation if a child fails. [S09]

Suspension timeline

A suspended task frees its thread for another task, then later resumes.
A conceptual single-thread timeline. Real dispatchers may resume on another thread. [S08]

Purpose synthesis

Distinguish suspension, thread choice, concurrency, and cancellation.

Mental model verified

suspend allows a function to suspend and resume. It does not automatically start a coroutine or move work off the current thread. Dispatchers determine execution. coroutineScope waits for children; failure in a child cancels its siblings and rethrows to the caller.

[S08] [S09]

Worked example synthesis

import kotlinx.coroutines.*

// Independent requests; APIs are supplied by the caller.
suspend fun load(profile: suspend () -> String,
                 offers: suspend () -> List<String>) = coroutineScope {
    val p = async { profile() }
    val o = async { offers() }
    p.await() to o.await()
}
[S08] [S09]

Decision + pitfall synthesis

Use sibling tasks when either failure invalidates the combined result. Consider supervision when failures are independent and handled. Blocking I/O or CPU work still needs an appropriate dispatcher. A detached application-global task can outlive the screen that requested it.

[S08] [S09]

Next improvement synthesis

Preserve CancellationException when catching broadly. Long CPU loops should check cancellation with ensureActive or another cooperative point. Use finally for owned resource cleanup and test cancellation before completion; do not silently turn cancellation into success.

[S10] [S11] [S23]
Keep this: Every long-lived task needs an owner, a cancellation path, and cleanup.
Check yourself: Does suspend fun guarantee background execution?

No. Thread choice comes from the coroutine context and explicit context switches.

Part 6 · Applications

Flow and StateFlow: work versus state

Prerequisites: 05-coroutines

Cold work and hot state

A cold flow starts work per collection; StateFlow supplies the current value to a new collector.
These are representative flow types. The Flow interface also has hot implementations. [S12] [S13]

Purpose synthesis

Choose the stream semantics that match a screen’s information needs.

Mental model verified

A flow built with flow { } is cold: its body runs when collected. StateFlow is hot, always has a current value, replays the latest state, and suppresses equal updates. Slow subscribers can miss intermediate values. StateFlow does not complete normally.

[S12] [S13]

Worked example synthesis

import kotlinx.coroutines.flow.*

class Counter {
    private val mutable = MutableStateFlow(0)
    val state: StateFlow<Int> = mutable.asStateFlow()
    fun increment() = mutable.update { it + 1 }
}
[S13]

Decision + pitfall synthesis

Use StateFlow for a renderable snapshot, not a payment audit trail. Choose explicit replay, buffering, and durability for events; a SharedFlow alone does not promise durable exactly-once delivery. Mutating a nested object in place may conceal a state change from equality-based observers.

[S12] [S13]

Next improvement synthesis

Tie collection to the consumer’s lifecycle. In the chosen Swift export route, verify the adapter’s cancellation behavior and stop collection on dismissal. Test late subscription and equal updates rather than requiring every intermediate render.

[S13] [S18] [S19] [S23]
Keep this: StateFlow describes what is true now; it does not preserve every event.
Check yourself: Will a slow StateFlow subscriber receive every counter value?

No. It may skip intermediate updates and receives the latest available state.

Part 7 · Applications

KMP: source sets become platform binaries

Prerequisites: 05-coroutines

Compile once per target

Common source branches into Android bytecode and DEX for ART, and native iOS machine code in an Apple framework.
Compilation arrows. The diagram omits intermediate compiler and linker stages, plus packaging and signing details. [S14] [S26] [S28] [S29]

Source visibility hierarchy

Android and iOS source sets depend on commonMain; device and simulator sources depend on iosMain.
Arrows mean dependsOn / access to parent declarations. They are deliberately reversed relative to the compilation-flow figure. [S14] [S15]

Purpose synthesis

Locate code correctly and trace its Android and iOS compilation paths.

Mental model verified

A target chooses a compilation platform. Source sets group code and dependencies. Android builds compile common and Android Kotlin through Kotlin/JVM; iOS builds compile common and iOS code with Kotlin/Native. A mobile shared module therefore produces different platform artifacts.

[S14] [S26]

Worked example synthesis

shared/src/commonMain/kotlin/Quote.kt
shared/src/androidMain/kotlin/AndroidTokenStore.kt
shared/src/iosMain/kotlin/IosTokenStore.kt
shared/src/commonTest/kotlin/QuoteTest.kt

// Apple Silicon simulator and iOS device are distinct targets:
// iosSimulatorArm64() and iosArm64()
[S14] [S15]

Decision + pitfall synthesis

Keep domain rules in commonMain. Android-only APIs belong in Android sources; Foundation APIs belong in Apple-compatible sources. iosMain can share implementations across device and simulator targets. A source set is not itself a deployable application.

[S14] [S15]

Next improvement synthesis

Build both platform artifacts early. Use the current compatibility guide for Kotlin, Gradle, AGP, and Xcode; migrate Android KMP library configuration using Google’s plugin guidance instead of copying stale androidTarget snippets. Dependency support must match every intended target.

[S24] [S15]
Keep this: commonMain is shared source compiled per target, not a runtime shared between phones.
Check yourself: Can commonMain import java.io.File when the module also targets iOS?

No. The JDK API is unavailable to the native compilation; use a common contract or multiplatform API.

Part 8 · Applications

Platform capabilities: contracts and adapters

Prerequisites: 07-compilation

Dependency inversion across platforms

QuoteService calls a common TokenStore interface; Android and iOS adapters implement it.
Solid arrow: dependency. Dashed arrows: implementation. Platform composition creates and injects the adapter. [S16]

Purpose synthesis

Share policy while supplying OS-specific behavior through a small seam.

Mental model verified

expect declares a common contract and actual supplies target implementations. Ordinary common interfaces plus injected platform implementations are another option. They are especially useful when you need multiple instances, fakes, or a platform-supplied service.

[S16]

Worked example synthesis

// commonMain: ordinary interface, no expect needed
interface TokenStore {
    suspend fun read(): String?
}
class QuoteService(private val tokens: TokenStore) {
    suspend fun hasSession(): Boolean = tokens.read() != null
}
// The app supplies AndroidTokenStore or IosTokenStore.
[S16]

Decision + pitfall synthesis

Use expect/actual for a small fixed platform primitive; prefer injection for a capability whose lifetime or implementation can vary. For token storage, choose OS-backed secure storage according to app requirements. Hiding plaintext preferences behind an interface does not make them secure. This is architectural synthesis, not a storage implementation.

[S16]

Next improvement synthesis

Create a fake TokenStore for common tests and run adapter integration tests on each platform. Verify failure, missing token, lifecycle, and cancellation. Keep Activity, UIViewController, and framework objects out of domain signatures.

[S16] [S22]
Keep this: Name the capability in common code; choose its concrete implementation at composition.
Check yourself: Must every shared interface use expect/actual?

No. An ordinary common interface can be implemented by platform code and injected.

Part 9 · Best practices

Choose the UI sharing boundary deliberately

Prerequisites: 08-ports, 06-flows

Two valid sharing choices

Platform UI or shared Compose UI both use a shared Kotlin feature layer.
Conceptual architectural alternatives. The diagram does not prescribe a rendering implementation or equalize platform behavior. [S17]

Purpose synthesis

Choose between shared logic with platform UI and additional shared Compose UI.

Mental model verified

KMP supplies cross-platform code sharing. Compose Multiplatform adds declarative shared UI and can coexist with existing platform UI. The shared logic / platform UI approach can use SwiftUI or UIKit on iOS and Android UI independently.

[S17] [S14]

Worked example synthesis

For a quote feature, share eligibility, parsing, and repository policy first. Keep an existing SwiftUI quote screen and Android screen. Alternatively, share that screen with Compose while retaining OS-specific camera, permissions, and app-shell integration. This is a proposed scope, not a guaranteed effort reduction.

[S17] [S16]

Decision + pitfall synthesis

Platform UI fits independent platform roadmaps and established teams. Shared UI fits substantial design overlap and coordinated releases. Compare accessibility, navigation, text input, and device integration with a realistic feature. Shared Compose UI does not mean every widget is a UIKit widget or that iOS engineering disappears.

[S17]

Next improvement synthesis

Pilot one screen with loading, error, background/foreground, keyboard, and accessibility paths. Measure the duplicate changes avoided against adapter and release costs before extending UI sharing.

[S17] [S22]
Keep this: Share the behavior that benefits the team; validate the experience on each OS.
Check yourself: Does adopting KMP force a UI rewrite?

No. Shared logic can serve existing platform UIs, and shared UI can be introduced incrementally.

Part 10 · Best practices

Swift interop: exported APIs need design

Prerequisites: 05-coroutines, 06-flows, 08-ports

Two export mechanisms

An Objective-C framework and Alpha Swift export expose different Swift API surfaces.
As verified on 2026-10-09. Callback/async and flow behavior are export-route dependent. Test cancellation propagation rather than inferring it. [S18] [S19]

Purpose synthesis

Choose an Apple export route and test its error, async, and cancellation behavior.

Mental model verified

Objective-C framework export exposes Kotlin APIs through Objective-C-compatible declarations. Its suspend APIs appear as completion handlers and can be callable as Swift async with documented limitations. Current Swift export instead generates Swift modules, supports suspend as async and Flow as AsyncSequence, but is Alpha and presently requires direct Xcode integration.

[S18] [S19]

Worked example synthesis

Export a small quote facade: loadQuote(id), a renderable state, and explicit close/cancel behavior if it owns work. Inspect the generated Swift surface. Test success, domain failure, early cancellation, and repeated subscribe/unsubscribe using the actual chosen route. The Kotlin declaration alone is not the contract test.

[S18] [S19] [S22]

Decision + pitfall synthesis

For Objective-C export, @Throws controls which expected exceptions become Swift errors; unexpected exceptions crossing the boundary can terminate the app. Keep common business failures explicit. Do not assume a Swift task’s cancellation cancels Kotlin work just because a call uses await. Verify the export route or wrapper.

[S18] [S10]

Next improvement synthesis

Current Swift export uses Dispatchers.Default by default for exported suspending work. UI delivery still needs the appropriate UI context. Hide internals, inspect generic/type mappings, and keep a Swift consumer regression test before compiler upgrades. Alpha features can change.

[S19] [S24]
Keep this: An API that is pleasant in Kotlin may need a narrower Swift-facing facade.
Check yourself: Does a Kotlin suspend declaration prove end-to-end Swift cancellation?

No. Cancellation propagation belongs to the generated or wrapped boundary and must be tested.

Part 11 · Best practices

Native memory, callbacks, and performance

Prerequisites: 10-swift-interop, 02-ownership

Mixed retention graph

Kotlin and Swift objects strongly reference one another; explicit detach or weak capture breaks ownership.
The sketch simplifies a mixed retain cycle. Pure Kotlin unreachable cycles can be collected; a graph involving ARC needs different care. [S20] [S21]

Purpose synthesis

Recognize ownership leaks and avoid treating interoperability as zero-cost.

Mental model verified

Kotlin/Native uses a shared heap and tracing GC. Swift/Objective-C objects use ARC. Mixed strong-reference cycles can prevent reclamation; interop object release can wait for GC. Thread-accessible objects are not automatically safe for unsynchronized mutation.

[S20] [S21]

Worked example synthesis

A Kotlin repository retains a Swift callback; the callback strongly captures a Swift screen owner that retains the repository. On dismissal, the feature stops its subscription and releases the callback. A weak Swift capture may also break the cycle. The right break point follows the ownership contract.

[S21]

Decision + pitfall synthesis

Use explicit close/detach semantics for owned listeners and resources. GC and ARC timing do not provide deterministic business cleanup. Avoid per-element cross-language calls in a hot path. Objective-C export can add string and collection conversion overhead; batching can help when it reduces conversions. Measure its memory and latency costs.

[S18] [S21]

Next improvement synthesis

Repeat open/close flows while profiling retained objects, allocation, and UI stalls on release builds. Inspect native GC diagnostics and Xcode Instruments. Do not tune collector options or apply old freezing recipes before identifying a real bottleneck. No benchmark result is claimed here.

[S20] [S21]
Keep this: Compiled native code still has a runtime, allocation costs, and ownership obligations.
Check yourself: Does Kotlin GC automatically reclaim a mixed ARC/Kotlin cycle?

No. Break the strong-reference cycle through the ownership design.

Part 12 · Further improvements

Validate on both targets, then expand

Prerequisites: 11-memory, 09-ui-boundary

Adopt through evidence

Common tests run on two targets, followed by boundary and platform tests and release metrics.
A proposed workflow. Testing raises confidence but does not establish identical platform behavior. [S22] [S23] [S24]

Purpose synthesis

Build a small adoption loop with portable tests, boundary tests, and release metrics.

Mental model verified

commonTest uses portable assertions, but tests execute through target-specific runners. Run shared tests on JVM and an iOS simulator. Platform tests cover adapters and OS behavior. runTest supports virtual-time coroutine tests; its usual single-thread scheduler does not prove safety under true parallel execution.

[S22] [S23]

Worked example synthesis

import kotlin.test.*

class DisplayNameTest {
    @Test fun blankMeansGuest() {
        assertEquals("Guest", displayName("  "))
    }
}
// Put this in commonTest and run it on each intended target.
// displayName is defined on sheet 01.
[S22] [S01]

Decision + pitfall synthesis

Adopt one self-contained rule or repository before expanding shared UI. Add a Swift consumer test for the exported framework. A successful JVM unit run cannot prove iOS linking, framework mappings, device permissions, or cancellation through Swift. Apple builds and signing need the Apple toolchain.

[S22] [S26] [S18]

Next improvement synthesis

Baseline clean and incremental build time, release artifact size, startup, screen latency, and crash rates. Pin a compatible toolchain and review upgrades together. Objective-C-export frameworks can be packaged as an XCFramework for SwiftPM distribution; do not assume Alpha Swift export supports that same route. Compare benefits against both platforms’ maintenance costs. These are proposed measures, not measured improvements.

[S24] [S25] [S19]
Keep this: Optimize the amount of trustworthy sharing, not the percentage of shared lines.
Check yourself: If common tests pass only on JVM, has the iOS integration passed?

No. Run Native-target tests plus framework and Swift consumer checks.

Sources & further reading

  1. [S01] Null safety

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Nullable types, smart casts, safe calls and Elvis operator

    Study the mechanism described above, then verify it against the versions used in your project.

  2. [S02] Collections overview

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Read-only versus mutable interfaces; val and mutable collections

    Study the mechanism described above, then verify it against the versions used in your project.

  3. [S03] Data classes

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Generated value operations and shallow copying

    Study the mechanism described above, then verify it against the versions used in your project.

  4. [S04] Sealed classes and interfaces

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Closed hierarchies and exhaustive when expressions

    Study the mechanism described above, then verify it against the versions used in your project.

  5. [S05] Higher-order functions and lambdas

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Function types, lambdas and passing behavior

    Study the mechanism described above, then verify it against the versions used in your project.

  6. [S06] Extensions

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Extension resolution and absence of real class modification

    Study the mechanism described above, then verify it against the versions used in your project.

  7. [S07] Collection transformation operations

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Transforming collection values

    Study the mechanism described above, then verify it against the versions used in your project.

  8. [S08] Coroutines basics

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Suspension, dispatchers, scopes and task hierarchy

    Study the mechanism described above, then verify it against the versions used in your project.

  9. [S09] coroutineScope API

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Scoped child tasks, waiting and failure propagation

    Study the mechanism described above, then verify it against the versions used in your project.

  10. [S10] CancellationException API

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Cancellation is a normal coroutine control signal

    Study the mechanism described above, then verify it against the versions used in your project.

  11. [S11] ensureActive API

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Cooperative checks in non-suspending work

    Study the mechanism described above, then verify it against the versions used in your project.

  12. [S12] Flows

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Cold flow execution and collection

    Study the mechanism described above, then verify it against the versions used in your project.

  13. [S13] StateFlow API

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Hot state, latest value, equality conflation and atomic update

    Study the mechanism described above, then verify it against the versions used in your project.

  14. [S14] Multiplatform project structure

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Common and platform sources compile together for a target

    Study the mechanism described above, then verify it against the versions used in your project.

  15. [S15] Source set hierarchy

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Intermediate iOS sources and target hierarchy

    Study the mechanism described above, then verify it against the versions used in your project.

  16. [S16] Expected and actual declarations

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Platform implementations and interface-based alternatives

    Study the mechanism described above, then verify it against the versions used in your project.

  17. [S17] Compose Multiplatform

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Optional shared UI and gradual adoption

    Study the mechanism described above, then verify it against the versions used in your project.

  18. [S18] Swift/Objective-C interoperability

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Framework export, exception mapping and conversion overhead

    Study the mechanism described above, then verify it against the versions used in your project.

  19. [S19] Swift export

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility · 2026-08-28

    Supports: Alpha status; async and AsyncSequence; limitations and direct integration

    Study the mechanism described above, then verify it against the versions used in your project.

  20. [S20] Kotlin/Native memory management

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Shared heap, tracing garbage collection and profiling

    Study the mechanism described above, then verify it against the versions used in your project.

  21. [S21] Integration with Swift/Objective-C ARC

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Mixed cycles, reclamation timing and completion thread caveats

    Study the mechanism described above, then verify it against the versions used in your project.

  22. [S22] Test your multiplatform app

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Common tests across targets and platform-specific validation

    Study the mechanism described above, then verify it against the versions used in your project.

  23. [S23] kotlinx-coroutines-test

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Virtual-time coroutine testing; runTest limitations

    Study the mechanism described above, then verify it against the versions used in your project.

  24. [S24] KMP compatibility guide

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Toolchain compatibility and Android plugin migration

    Study the mechanism described above, then verify it against the versions used in your project.

  25. [S25] Swift package export setup

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: XCFramework binary distribution through SwiftPM

    Study the mechanism described above, then verify it against the versions used in your project.

  26. [S26] Kotlin/Native overview

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Native compilation and runtime overview

    Study the mechanism described above, then verify it against the versions used in your project.

  27. [S27] Calling Java from Kotlin

    JetBrains / Kotlin project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Platform types and nullability at a Java boundary

    Study the mechanism described above, then verify it against the versions used in your project.

  28. [S28] d8 Android dex compiler

    Google / Android Developers · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: Java bytecode to DEX compilation in Android builds

    Study the mechanism described above, then verify it against the versions used in your project.

  29. [S29] Android runtime and Dalvik

    Android Open Source Project · official documentation · accessed 2026-10-09 · Rolling documentation as accessed; use project-specific toolchain compatibility

    Supports: ART executes DEX code on Android

    Study the mechanism described above, then verify it against the versions used in your project.