Skip to content

[android][breaking] compile with kotlin 2.x and raise api / language version#75

Merged
kiftio merged 1 commit into
mainfrom
compile-with-kotlin2.x
May 26, 2026
Merged

[android][breaking] compile with kotlin 2.x and raise api / language version#75
kiftio merged 1 commit into
mainfrom
compile-with-kotlin2.x

Conversation

@kiftio
Copy link
Copy Markdown
Contributor

@kiftio kiftio commented May 13, 2026

Summary

Bumps the Kotlin compiler (and serialization plugin) from 1.9.23 to 2.3.21, and raises the consumer-floor pin (apiVersion / languageVersion) from Kotlin 1.9 to Kotlin 2.0.

This is a breaking change for consumers. Raising apiVersion / languageVersion makes Kotlin 2.0+ the required consumer floor — anyone still building against 1.9 can't consume the new AAR. Bundling this into the next major release is deliberate (see below).

First of a stack of PRs preparing the next major release.

If you consume this library and you're stuck on Kotlin 1.9.x for any reason — please reach out before this lands. We chose the lowest pin we believe the consumer base can absorb, but the data on real-world consumer floors is limited; concrete blockers will help us calibrate.

Why now

  • Kotlin 1.9 is deprecated by the 2.x compiler and will be removed in a future minor. Holding the consumer pin at 1.9 forces us to stay on a 1.9.x compiler indefinitely, which is itself a dead-end path.
  • One forced consumer-floor break, not two. Doing the compiler bump and the apiVersion/languageVersion bump together means consumers cross the breaking boundary once. If we bumped just the compiler now and the pin later, consumers would absorb two separate "minimum Kotlin" raises in successive releases.
  • Major-version cut is the cheapest moment to do it. A consumer-floor raise is already a major-version event; bundling it into a planned major (alongside JVM 11) avoids spending a major version on this alone.
  • Compiler ahead of the pin (compiler 2.3, pin 2.0) leaves room for future routine compiler bumps without re-breaking consumers.

Why pin 2.0 and not 2.1

We considered raising the pin to Kotlin 2.1 — it would silence a compiler warning we now carry (see below) and buy more runway before the next forced break. Pin 2.0 was the deliberate choice for consumer reach:

  • React Native consumers: the RN wrapper at platforms/react-native/ claims react-native: "*" peer compatibility. RN templates ship Kotlin 1.9 on RN ≤ 0.76, Kotlin 2.0 on RN 0.77/0.78, Kotlin 2.1+ on RN 0.79+. Pin 2.0 keeps RN 0.77+ consumers reachable; pin 2.1 would cut off two RN minors that are still very common in production given how painful RN upgrades are.
  • Unknown consumer toolchains: we don't have systematic visibility into top consumers' Kotlin/AGP floors. Pin 2.0 is the most conservative non-deprecated choice available.

Accepted trade-off: LV 2.0 deprecation warning in our build

Kotlin 2.3's compiler now emits Language version 2.0 is deprecated and its support will be removed in a future version of Kotlin. Update the version to 2.1. on every :lib:compile*Kotlin invocation. We accept this warning rather than bump the pin to 2.1:

  • The warning is in our build only — consumers don't see it. They consume the published AAR; Kotlin metadata is what they read, and the metadata is valid for any Kotlin 2.0+ compiler.
  • It will become a hard error in some future Kotlin minor. When it does, we'll bump the pin in a minor release of the library, by which time the lagging RN/consumer cohort will have caught up and the cost will be lower.
  • No lib.api impact from the 2.3 compiler bump (verified — apiDump produced zero diff vs the 2.1.20 dump).

Changes

  • build.gradle: Kotlin plugins (org.jetbrains.kotlin.android, org.jetbrains.kotlin.plugin.serialization) 1.9.23 → 2.3.21.
  • lib/build.gradle: apiVersion / languageVersion pinned to KotlinVersion.KOTLIN_2_0; explanatory comment updated.
  • gradle.properties (root + sample): AGP-opt-out comment block rewritten. android.builtInKotlin=false and android.newDsl=false remain, now documented as tooling-gap workarounds (BCV #312, KT-71172) rather than as Kotlin-version-specific.
  • lib/api/lib.api: regenerated. 780 / 780 line diff, all inside auto-generated kotlinx-serialization $$serializer companion classes flipping to synthetic with final methods (a language-2.0 codegen change, not a human-authored API change).
  • CLAUDE.md: references to the Kotlin pin updated 1.9 → 2.0.

Why we're not on AGP 9's built-in Kotlin

AGP 9 ships a built-in Kotlin plugin that replaces org.jetbrains.kotlin.android, but neither of our API-validation paths works with it for Android libraries today:

  • binary-compatibility-validator listens for the kotlin-android plugin id, which AGP's built-in path doesn't apply (BCV #312) — :lib:apiCheck/:lib:apiDump silently never register, so our public-API gate disappears.
  • KGP's experimental abiValidation DSL exists for JVM and Multiplatform but is not yet exposed on KotlinAndroidProjectExtension (KT-71172).

We stay on the explicit org.jetbrains.kotlin.android plugin so BCV keeps gating public API. The two AGP opt-outs (builtInKotlin=false, newDsl=false) are coupled to this decision — revisit when either upstream issue lands. Flipping them later is consumer-invisible: same kotlinc output, same lib.api.

Consumer impact

  • Consumer floor (breaking): Kotlin 2.0+ now required to consume the AAR. Consumers on 1.9.x toolchains can't build against the new release. Needs a prominent release-notes entry.
  • React Native consumers: RN 0.77+ apps (which ship Kotlin 2.0+) build fine. RN ≤ 0.76 (Kotlin 1.9) can't consume the new lib until they upgrade RN.
  • Java consumers: effectively unaffected by the Kotlin pin itself — Java doesn't read Kotlin metadata. Only transitive impact is kotlin-stdlib floor on the classpath rising from 1.9.x → 2.0.x. JVM target / JDK floor changes live in the stacked target-jvm-11 PR.
  • lib.api: 780-line diff, all inside auto-generated kotlinx-serialization $$serializer companions — they flip to synthetic with final methods under language-2.0. No human-authored API moved.
  • Practical day-to-day: zero for normal consumers already on Kotlin 2.0+. Json.encodeToString(...), Foo.serializer() and friends behave identically. Only theoretically affected: anyone subclassing an auto-generated $$serializer (not a supported kotlinx-serialization pattern).

Test plan

  • ./gradlew :lib:clean :lib:test :lib:detekt :lib:lintRelease :lib:apiCheck :lib:assembleRelease — green
  • InteropTest (Java consumer test) — all cases pass
  • Sample app ./gradlew assembleDebug from samples/MobileBuyIntegration/ — green

Test plan

  • ./gradlew :lib:clean :lib:test :lib:detekt :lib:lintRelease :lib:apiCheck :lib:assembleRelease — green
  • InteropTest (Java consumer test) — all cases pass
  • Sample app ./gradlew assembleDebug from samples/MobileBuyIntegration/ — green

Test plan

  • ./gradlew :lib:clean :lib:test :lib:detekt :lib:lintRelease :lib:apiCheck :lib:assembleRelease passes locally.
  • Java InteropTest passes (consumer-facing Java interop intact).
  • :lib:apiCheck still gates public-API drift against lib/api/lib.api.
  • CI green on the PR.

Before you merge

Important

  • I've added tests to support my implementation
  • I have read and agree with the Contribution Guidelines
  • I have read and agree with the Code of Conduct
  • I've updated the relevant platform README (platforms/swift/README.md and/or platforms/android/README.md)

Releasing a new Swift version?
  • I have bumped the version in platforms/swift/ShopifyCheckoutKit.podspec
  • I have bumped the version in platforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swift
  • I have updated platforms/swift/CHANGELOG.md
  • I have updated the SwiftPM/CocoaPods version snippets in platforms/swift/README.md (major version only)
Releasing a new Android version?
  • I have bumped the versionName in platforms/android/lib/build.gradle
  • I have updated platforms/android/CHANGELOG.md
  • I have updated the Gradle/Maven version snippets in platforms/android/README.md

Tip

See the Contributing documentation for the full release process per platform.

@kiftio kiftio requested a review from a team as a code owner May 13, 2026 09:25
@kiftio kiftio force-pushed the compile-with-kotlin2.x branch 2 times, most recently from 6c52e72 to 1d113b1 Compare May 13, 2026 11:58
@kiftio kiftio changed the title compile with kotlin 2.x [android][breaking] compile with kotlin 2.x and raise api / language version May 13, 2026
Copy link
Copy Markdown
Contributor Author

kiftio commented May 22, 2026

@kiftio kiftio mentioned this pull request May 22, 2026
15 tasks
@kiftio kiftio force-pushed the compile-with-kotlin2.x branch from 1d113b1 to f5ed660 Compare May 22, 2026 13:17
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 22, 2026

React Native — Coverage Report

Lines Statements Branches Functions
Coverage: 98%
96.76% (239/247) 91.02% (142/156) 100% (64/64)

@kiftio kiftio force-pushed the compile-with-kotlin2.x branch from f5ed660 to baeaca6 Compare May 26, 2026 09:25
@kiftio kiftio force-pushed the compile-with-kotlin2.x branch from baeaca6 to d6d3dbf Compare May 26, 2026 09:52
Copy link
Copy Markdown
Contributor Author

kiftio commented May 26, 2026

Merge activity

  • May 26, 10:31 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 26, 10:32 AM UTC: @kiftio merged this pull request with Graphite.

@kiftio kiftio merged commit 79de76a into main May 26, 2026
22 checks passed
@kiftio kiftio deleted the compile-with-kotlin2.x branch May 26, 2026 10:32
This was referenced May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants