All Articles
Reading Time
15 min read
Published
2026-10-13
Word Count
3,447words

Grab a coffee — this one is a deep dive!

Compose Multiplatform 1.12: Shared UI from Android to iOS

Summary

Compose Multiplatform 1.12 iOS/Android shared UI: the 1.12.0 vs 1.12.1 version delta, the Kotlin 2.1.0+ alignment rule, migration steps, and what changed in iOS behavior.

  • Compose Multiplatform 1.12.0 (August 25, 2026) and 1.12.1 (September 22, 2026) shipped back to back; 1.12.1 is just a maintenance patch.
  • Minimum Kotlin version is 2.1.0; if you target iOS/web, JetBrains recommends Kotlin 2.2.20 or above.
  • The Compose Compiler Gradle plugin must be on the EXACT same version as the Kotlin Multiplatform plugin.
  • 1.12.1 fixes accumulated iOS bugs like toolbar visibility and an AccessibilityElement dispose crash.
Compose Multiplatform 1.12: Shared UI from Android to iOS

Compose Multiplatform 1.12 is JetBrains' latest stable release line bringing Jetpack Compose from Android to iOS, web, and desktop — 1.12.0 shipped on August 25, 2026, and the 1.12.1 patch followed on September 22, 2026. If your project is still on 1.11, this article walks you through the version delta, the Kotlin alignment rule, and the migration steps; for architectural decisions and a production case study, see Compose Multiplatform: Android + iOS production deployment. Here's exactly what changed on the Compose Multiplatform 1.12 iOS/Android shared-UI side.

💡 Pro Tip: Always keep the Compose Compiler Gradle plugin at the EXACT same version as the Kotlin Multiplatform plugin; the Compose compiler ships in lockstep with Kotlin and is always compatible with the matching Kotlin version — in multiplatform projects with non-JVM targets, older dependencies built with Compose compiler 2.0.0 can trigger unnecessary, even infinite, recompositions.

Table of Contents

The 1.12 version delta

Compose Multiplatform isn't a single "1.12" — it's two releases that arrived in quick succession: 1.12.0, the main release, and 1.12.1, a maintenance patch that followed a month later. Deciding which one to pull into your project is a matter of checking the table below.

Version
Release date
Highlight
Notes
1.12.0
August 25, 2026
MCP server for Compose Hot Reload (desktop) — lets AI agents interact in real time with a running Compose app
Main feature release
1.12.1
September 22, 2026
Kotlin 2.5 compatibility fix in the resources library (PR #5717), multiple iOS crash/visibility fixes
Patch release titled "Changes since 1.12.0"

The 1.12.1 release notes also pin the Gradle plugin version for org.jetbrains.compose to the exact 1.12.1 — your build.gradle.kts needs to match this patch number. The Kotlin 2.5 compatibility fix stands out because Kotlin 2.5.0 hasn't shipped yet (planned for December 2026) — notably, it sits under "Fixes → Resources," so it's specific to the resources library. JetBrains added it as forward-looking preparation, not a current requirement.

The four-week gap between the two releases is itself a signal: 1.12.0 is a major feature release, while 1.12.1 is made up almost entirely of fixes filed under "Changes since 1.12.0," its single migration item being the deprecation of LocalSystemTheme (covered below). So it makes sense to move to 1.12.1 purely for stability, without expecting any architectural change — I'd recommend picking up this patch even before a larger Kotlin or Gradle jump, since its iOS fixes are backward compatible and introduce no new behavior change.

The Kotlin/Compose version alignment rule

Compose Multiplatform's relationship with Kotlin versions isn't a fixed mapping table — it's a floating minimum rule: the latest Compose Multiplatform is always compatible with the latest Kotlin version. The official compatibility page states this plainly and also gives you a practical lower bound for your project.

Component
Minimum version
Recommended
Kotlin (general)
2.1.0
2.4.20 (current stable, September 7, 2026)
Kotlin (iOS/web targets)
2.1.0
2.2.20+
Compose Compiler Gradle plugin
Same as Kotlin Multiplatform plugin
Same

Since version 1.8.0, Compose Multiplatform has fully moved the UI framework to the K2 compiler; the 1.12 line builds on that same foundation. You set up this alignment in your Gradle file like this:

kotlin
1plugins {
2 kotlin("multiplatform") version "2.4.20"
3 id("org.jetbrains.compose") version "1.12.1"
4 id("org.jetbrains.kotlin.plugin.compose") version "2.4.20" // SAME version as the Kotlin MP plugin
5}

If you want to manage this alignment from one place in a multi-module project, use a Gradle version catalog (libs.versions.toml); both plugins then reference the same kotlin version entry:

toml
1[versions]
2kotlin = "2.4.20"
3compose-multiplatform = "1.12.1"
4 
5[plugins]
6kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
7kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
8compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }

The Compose Multiplatform release cycle generally trails the Jetpack Compose (androidx) release cycle by 1-3 months — a new Jetpack Compose version on Android doesn't land in Compose Multiplatform immediately; it takes anywhere from a few weeks to a few months.

In practice this floating rule means two things. First: instead of memorizing a mapping table like "Compose Multiplatform X requires Kotlin Y," re-check the official compatibility page on every upgrade, since the minimum version keeps drifting upward. Second: don't expect a newest Jetpack Compose feature to be immediately usable in Compose Multiplatform (especially on iOS/web) — build that 1-3 month lag into your project planning.

Upgrading an existing project to 1.12

Moving from 1.11 to 1.12.1 starts with updating your library coordinates. Watch out for the most common mistake here: the coordinates in the Components table of the 1.12.1 release notes are NOT UNIFORM. runtime, ui, foundation, and material ship tagged 1.12.1, while Material3 stays at 1.12.0-alpha03, Material3 Adaptive at 1.3.0-rc01, Lifecycle at 2.11.0, and Navigation at 2.10.0-beta01 — so you need to copy each line straight from the table. In your shared UI module, you declare these dependencies through the commonMain source set:

kotlin
1kotlin {
2 sourceSets {
3 commonMain.dependencies {
4 implementation("org.jetbrains.compose.runtime:runtime:1.12.1")
5 implementation("org.jetbrains.compose.ui:ui:1.12.1")
6 implementation("org.jetbrains.compose.foundation:foundation:1.12.1")
7 implementation("org.jetbrains.compose.material3:material3:1.12.0-alpha03")
8 }
9 }
10}

If you run into compatibility issues with older libraries (especially third-party Compose components), you can disable the compilation cache with the disableNativeCache option in the Kotlin/Native target's binaries block. The official Multiplatform DSL reference says to use this option only in exceptional cases, since it lengthens build time; the option requires you to pass the Kotlin version at which the cache is disabled and a reason as arguments, with an issue URL being optional:

kotlin
1@file:OptIn(KotlinNativeCacheApi::class)
2 
3import java.net.URI
4import org.jetbrains.kotlin.gradle.plugin.mpp.DisableCacheInKotlinVersion
5import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCacheApi
6 
7kotlin {
8 iosArm64 {
9 binaries {
10 framework("shared") {
11 disableNativeCache(
12 version = DisableCacheInKotlinVersion.`2_4_20`,
13 reason = "Cache bug",
14 issue = URI("https://youtrack.com/YY-1111")
15 )
16 }
17 }
18 }
19}

The DisableCacheInKotlinVersion constant list only carries the last three Kotlin versions (rolling deprecation), so you'll need to update the constant whenever you upgrade Kotlin; the whole API also requires opting in with @OptIn(KotlinNativeCacheApi::class).

Another migration detail: the LocalSystemTheme API was temporarily re-exposed in 1.12.1 as @InternalComposeUiApi, but deprecated at the same time — it will be removed entirely in a future release. Because it's marked internal, @Suppress("DEPRECATION") alone isn't enough; you also need @OptIn(InternalComposeUiApi::class). If you're still using it in your code, you should plan a migration to platform-specific alternatives for reading the system theme. This API is defined on the skiko side (iOS/desktop/web), not on Android — put it in a platform source set like iosMain/jvmMain, not commonMain:

kotlin
1import androidx.compose.runtime.Composable
2import androidx.compose.ui.InternalComposeUiApi
3import androidx.compose.ui.LocalSystemTheme
4import androidx.compose.ui.SystemTheme
5 
6// Re-opened as @InternalComposeUiApi and deprecated in 1.12.1, will be removed in the future
7@OptIn(InternalComposeUiApi::class)
8@Suppress("DEPRECATION")
9@Composable
10fun isSystemDark(): Boolean = LocalSystemTheme.current == SystemTheme.Dark

Rather than upgrading every module at once, try it first in the shared UI module, and once builds and tests pass, move on to platform-specific targets (iosMain, jvmMain). The first thing to do after changing the library coordinates is a full clean build (./gradlew clean build) — if there's a drift between the Compose Compiler and Kotlin plugin versions, it becomes visible at this step, and you catch it before it reaches production.

What changed on iOS

1.12.0 and 1.12.1 brought both new behaviors and accumulated bug fixes on the iOS side:

  • Selection handles: as of 1.12.0, these now match iOS's native style.
  • Context menu "Select": an iOS-specific "Select" item was added to the context menu in text fields.
  • VoiceOver LiveRegion support: LiveRegion semantics are now supported on the accessibility side, improving dynamic content announcements for VoiceOver users.
  • NSLocale.toComposeLocale(): 1.12.0 added this API for constructing a Locale from the platform's NSLocale object (PR #3103).
  • VerbatimTtsAnnotation and LocaleList in VoiceOver: 1.12.0 added support for these two attributes on the accessibility VoiceOver side (PR #3145).
  • Toolbar not appearing bug (iOS 16 and earlier): fixed in 1.12.1.
  • SelectionContainer + TextField toolbar bug: fixed the issue where the toolbar wouldn't appear when a SelectionContainer containing at least one TextField was used.
  • Crash after AccessibilityElement dispose: fixed a crash on iOS caused by trying to read the properties of a disposed AccessibilityElement.

This list matters especially for screens dealing with accessibility and text selection — if you wrote workarounds for these scenarios in earlier versions, you'll need to remove and re-test them once you move to 1.12.1.

Practically speaking, most of these changes don't open a new API surface — they bring existing behavior closer to what iOS natively expects; new bridge APIs like NSLocale.toComposeLocale() are opt-in. So there's no mandatory migration step — the real work is regression testing. If you have a screen that uses VoiceOver, the most reliable way to catch behavior differences that an automated test suite would miss is to turn on VoiceOver on a real iOS device after moving to 1.12.1 and manually walk through the selection and context-menu flows.

Performance and startup time measurement

JetBrains hasn't published an official startup time or performance benchmark for the 1.12 series; there's no numeric improvement claim in the release notes or the compatibility page. You'll need to measure this yourself in your own project.

Instead, measure it yourself: compare cold-start times before and after moving from 1.11 to 1.12.1 using Macrobenchmark on Android and Xcode Instruments' "App Launch" template on iOS. I generally prefer a baseline on the current version before a major jump, then re-measuring on the same device after — the only reliable way to produce data JetBrains hasn't published.

A few rules make the measurement meaningful: use the same physical device (not a simulator/emulator), close other background apps, take at least 5-10 repeated runs and report both average and median (a single run is noise-prone), and measure cold-start and warm-start separately. Without this discipline, an "it got faster after moving to 1.12" impression could just be thermal or background-process noise.

When sharing results with your team, also note the device model, OS version, and measurement date — six months from now, when you're trying to answer "did it actually get faster after we moved to 1.12?", the numbers lose their meaning without this context.

When to use CMP, when to use native SwiftUI

JetBrains' official platform support table uses clear labeling: the iOS target is "Stable," while the Web target is still "Beta." This is an important signal when making a decision.

Compose Multiplatform's core promise is letting you use your existing Jetpack Compose knowledge (state management, the layout system, animation APIs, Material components) on platforms other than Android. If your team has already invested in Compose and the same team will be writing code for iOS too, CMP is a sensible choice.

Prefer native SwiftUI when platform-specific fine-tuning is critical: iOS-specific animation details, day-one access to the newest Apple APIs (e.g. widget/interactivity features that arrive with a new SwiftUI release), or a team that already has deep Swift/SwiftUI expertise. JetBrains doesn't publish an official "use CMP here, use native there" matrix; it's a balance based on your team's existing investment and the density of platform-specific requirements.

In practice, two questions are enough: Is your team currently writing Compose on Android, with the iOS team working separately? If so, CMP's shared-code advantage may run into an organizational barrier. Second: does your app depend on an iOS-specific, platform-native feel (e.g., gesture-based navigation), or does it mostly rely on standard UI patterns like forms, lists, and data display? If the latter, CMP's Stable-labeled iOS target is generally enough.

Known limitations and workarounds

There are a few structural limits you should know before upgrading:

  • JDK requirement: the Compose Multiplatform desktop target only supports JDK 11 or above; for native distribution packaging (due to jpackage limitations), JDK 17 or above is required.
  • macOS keyboard layout bug: there's a known issue with OpenJDK 11.0.12 when switching keyboard layouts on macOS; this issue doesn't reproduce on OpenJDK 11.0.15 — if you're still on 11.0.12, an update is recommended.
  • 64-bit only: all Compose Multiplatform versions only support 64-bit platforms — if you need to target 32-bit, this framework isn't suitable.

These limits matter for teams setting up CI/CD: adding a native packaging step (.dmg, .msi, .deb) without JDK 17+ on the build agent can make jpackage fail silently. Pinning the macOS build agent's OpenJDK version (11.0.15+, or 17+ directly) should be standard for the same reason.

Platform
Minimum version (1.12.1)
Android
5.0 (API level 21)
iOS
14
macOS
13 (arm64)
Windows
10 (x86-64, arm64)
Linux
Ubuntu 20.04

GOLDEN TIP

The most valuable insight in this article

This tip holds the article's most important takeaway.

Easter Egg

You found a hidden gem!

There's a hidden detail in this section. Want to uncover it?

Reader Reward

If you've read this article this far, you've earned a step-by-step checklist you can use for migrating from 1.11 to 1.12.1. Apply the items below in order, and build and test the project after each step.

FAQ

Which Kotlin version does Compose Multiplatform 1.12 require?

Compose Multiplatform 1.12.x (1.12.0 and 1.12.1) requires a minimum of Kotlin 2.1.0. If you're targeting fast-moving platforms like iOS and web, JetBrains recommends upgrading to Kotlin 2.2.20 or above. This rule isn't fixed: the official compatibility page says "The latest Compose Multiplatform is always compatible with the latest version of Kotlin," so using the current Kotlin (2.4.20, September 7, 2026) on a per-project basis is the safer choice.

Is Compose Multiplatform production-ready on iOS?

Yes — Compose for iOS has been labeled "Stable" since version 1.8.0, and the 1.12 series added new iOS-specific improvements on top of that (selection handles, context menu, VoiceOver LiveRegion support). The Web target, on the other hand, is still labeled "Beta" — you need to make the production call separately per platform.

How do Compose Multiplatform versions align with Jetpack Compose versions?

Compose Multiplatform is built on top of Jetpack Compose, and each component library ships tagged with a specific version — in 1.12.1, runtime, ui, foundation, and material are published under the org.jetbrains.compose.*:1.12.1 coordinate, while material3 stays at the 1.12.0-alpha03 coordinate. The Compose Multiplatform release cycle generally trails the Jetpack Compose release cycle by 1-3 months.

What does it cost to move existing Android Compose code to iOS?

JetBrains recommends gradual adoption and describes it as a scale: "Start small or go all in without extra work" — meaning you can share a single component, a single screen, or the entire UI, and it fits into your existing app. The official sources don't give a concrete day/hour figure; the real cost depends on how cleanly the project separates UI from business logic — if that separation isn't clean, shared composables can quickly get tangled with platform-specific branching.

What's the difference between 1.12.0 and 1.12.1?

1.12.0 arrived on August 25, 2026 as the main feature release (including the MCP server for Compose Hot Reload on desktop). 1.12.1, released on September 22, 2026, is a maintenance patch: it includes Kotlin 2.5 compatibility preparation in the resources library and multiple iOS crash/visibility fixes, without adding any new major feature.

Can I keep the Compose Compiler plugin version different from the Kotlin plugin version?

No — the official documentation explicitly states that the Compose Compiler Gradle plugin must be applied at the same version as the Kotlin Multiplatform plugin. Using different versions is an unsupported configuration.

Which JDK version does desktop support in Compose Multiplatform need?

JDK 11 minimum is enough for the desktop target, but that's only for runtime. If you're going to package your app as a native distributable like .dmg, .msi, or .deb, JDK 17 or above is required — this limitation comes from the jpackage tool's own requirements. Not overlooking this distinction when choosing your build agent in a CI pipeline prevents the packaging step from failing silently.

Conclusion

The Compose Multiplatform 1.12 series is an update that prioritizes version-alignment discipline and accumulated iOS bug fixes over any major architectural leap. 1.12.0's headline feature is the MCP server for Compose Hot Reload on desktop, while 1.12.1 arrived with Kotlin 2.5 preparation in the resources library and iOS crash fixes. The most critical step when upgrading is keeping the Compose Compiler plugin at the same version as the Kotlin Multiplatform plugin.

For architectural decisions and a larger production case study, see Compose Multiplatform: Android + iOS production deployment; for a similar version-migration experience on the Kotlin Multiplatform side, see Kotlin Multiplatform 1.1 Stable production case study. If you're curious about the performance side of Jetpack Compose on Android, Jetpack Compose 1.7 strong skipping and, for K2 compiler details, Kotlin 2.1 K2 compiler and context parameters round things out. If you want to see a comparison with native SwiftUI at a larger production scale, Flutter vs SwiftUI 60K LOC comparison is also a useful reference.

Sources

Tags

#Compose Multiplatform#Jetpack Compose#Kotlin#iOS#Android#KMP#Cross-Platform
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

Lead Mobile Engineer with 12+ years of experience. Expert in iOS, Android and cross-platform architectures with Swift, SwiftUI, Kotlin and Flutter. I build performant, user-friendly mobile apps.

iOS Development News

Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.

Your subscription starts when you open the link in the confirmation email and press “Confirm my subscription”. The newsletter keeps open/click statistics; you can unsubscribe anytime with one click. Privacy

Share