skip to content

Android & iOS Targets

The Android target produces an AAR against the Android SDK, while the iOS targets produce a framework you consume from Swift, optionally packaged via CocoaPods or SwiftPM. This pair is the reason most teams look at KMP at all.

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

questions

5

In a Kotlin Multiplatform module, what artifact does the androidTarget() produce versus the iosArm64()/iosSimulatorArm64() targets, and how does each get consumed?

level: juniorimportance: must knowfreq 62%

answer

  1. androidTarget() -> AAR over Android SDK
  2. iosArm64 = device, iosSimulatorArm64 = Apple-silicon simulator
  3. iOS targets are Kotlin/Native -> .framework
  4. commonMain shared, androidMain/iosMain actuals
  5. android() renamed androidTarget() in 1.9

basics

~10 s

androidTarget() builds an Android library (an AAR) that Android apps use. The iOS targets build a native framework that an Xcode/Swift app imports. Same Kotlin code, two different output packages.

solid answer

~30 s

In the kotlin { } block, androidTarget() configures compilation against the Android SDK and produces an Android Archive (AAR) consumed via Gradle by an Android app module. iosArm64() (real devices) and iosSimulatorArm64() (Apple-silicon simulator) are Kotlin/Native targets that compile to a .framework (or XCFramework) which Xcode/Swift imports. The shared module declares these targets; commonMain holds shared code, androidMain and iosMain hold platform-specific actual implementations of expect declarations. Android consumes the AAR like any Gradle dependency; iOS links the framework. The same source set feeds both, so business logic is written once and packaged twice for the two platforms.

code

kotlin · 11 lines
kotlin
kotlin {
    androidTarget()          // -> AAR consumed by Android app
    iosArm64()               // -> device framework
    iosSimulatorArm64()      // -> Apple-silicon simulator framework

    sourceSets {
        commonMain.dependencies { /* shared deps */ }
        androidMain.dependencies { /* Android deps */ }
        iosMain.dependencies { /* iOS deps */ }
    }
}

go deeper

for a junior

Knows androidTarget() makes an AAR and iOS targets make a framework, and that code is shared via commonMain.

for a middle

Distinguishes device vs simulator targets and explains expect/actual feeding both artifacts.

for a senior

Explains JVM-backend vs Kotlin/Native-backend differences and consumption paths (Gradle dependency vs Xcode linking).

for a principal

Frames target selection as a packaging/distribution decision and can reason about XCFramework vs single framework trade-offs.

## What a KMP target is A **target** in Kotlin Multiplatform (KMP) is a single platform you compile your shared code for. You declare targets inside the `kotlin { }` DSL of a Gradle build file. Each target pulls in a platform-specific compiler backend and produces a platform-native artifact. ## androidTarget() - `androidTarget()` compiles your Kotlin against the **Android SDK** using the JVM backend (Android runs Kotlin/JVM bytecode, later turned into DEX by the Android toolchain). - It requires the **Android Gradle Plugin (AGP)** to be applied (`com.android.library`). - The output is an **AAR (Android Archive)** - a zip containing compiled classes plus Android resources/manifest - which an Android app module consumes as a normal Gradle dependency. - Renamed from the old `android()` to `androidTarget()` in Kotlin 1.9 to free up `android` for a future Android-specific DSL. ## iosArm64() and iosSimulatorArm64() These are **Kotlin/Native** targets - they compile straight to machine code via LLVM, no JVM involved. - `iosArm64()` -> ARM64 binary for **real iPhones/iPads**. - `iosSimulatorArm64()` -> ARM64 binary for the **iOS Simulator running on Apple-silicon Macs**. - (`iosX64()` exists for Intel-Mac simulators.) - Output is an Apple **`.framework`** (or several combined into an **XCFramework**) that an Xcode project imports and Swift/Objective-C code calls. ## Source sets tie them together ```kotlin kotlin { androidTarget() iosArm64() iosSimulatorArm64() sourceSets { commonMain.dependencies { /* shared */ } androidMain.dependencies { /* Android-only */ } iosMain.dependencies { /* iOS-only */ } } } ``` `commonMain` is the shared code. `expect` declarations there get platform `actual` implementations in `androidMain` / `iosMain`. The compiler produces the AAR and the framework from the same common source plus the platform source set. ## How each is consumed - **Android:** add the shared module as a Gradle dependency; Gradle resolves the AAR. - **iOS:** link the framework into the Xcode app (manually, or via CocoaPods/SwiftPM packaging).

  • Why was android() renamed to androidTarget()?
    To reserve the cleaner android name for a future Android-specific DSL; androidTarget() is the current spelling since Kotlin 1.9.
  • Do iOS targets use the JVM?
    No. They are Kotlin/Native targets compiled to native machine code via LLVM; there is no JVM or JVM-style runtime.

One recipe (commonMain), two kitchens: the Android kitchen plates an AAR, the iOS kitchen plates a framework.

saying these in an interview costs you the question

  • Says iOS targets produce a JAR or run on a JVM
  • Thinks androidTarget() outputs an APK rather than an AAR library
  • Confuses iosArm64 (device) with the simulator target
  • Believes you must duplicate business logic per platform

context

open as a page

Compare the two main ways to package a KMP framework for iOS consumers: the CocoaPods Gradle plugin versus the Swift Package Manager (SwiftPM) integration. What does each generate and when would you pick one?

level: middleimportance: should knowfreq 40%

basics

~20 s

The CocoaPods plugin makes your shared module a Pod the iOS app pulls via a Podfile. The SwiftPM route publishes the framework as an XCFramework referenced by a Package.swift. Pick CocoaPods if the app already uses Pods; SwiftPM for a Pod-free, native Apple toolchain.

open as a page

How do you configure the iOS framework output from a KMP module using the framework DSL inside binaries.framework {} - name, static vs dynamic, baseName, and exporting dependencies?

level: middleimportance: should knowfreq 48%

basics

~20 s

Each iOS target has a binaries.framework {} block where you set the framework name, choose static or dynamic linking, and export other KMP libraries so their public API also shows up in the Swift-facing framework.

open as a page

A teammate ships a single iosArm64 .framework and the app fails to run on the Apple-silicon simulator. Explain the architecture problem and how XCFramework plus the right targets fix it.

level: seniorimportance: should knowfreq 38%

basics

~20 s

iosArm64 is for real devices only. The simulator on an Apple-silicon Mac needs iosSimulatorArm64. A plain .framework holds one slice, so the simulator can't link it. Build both targets and bundle them in an XCFramework, which carries multiple slices.

open as a page

When wiring androidTarget() in a KMP module, what AGP setup and source-set/configuration details matter for producing a usable AAR (compileSdk, namespace, publishLibraryVariants, dependency configurations, manifest)?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

You apply the Android library Gradle plugin, set compileSdk/minSdk and a namespace in the android {} block, and put Android-only code in androidMain. androidTarget() then builds an AAR that an Android app can depend on. You can pick which build variants get published.

open as a page