The iOS side of Kotlin Multiplatform has carried the same complaint for years: APIs that pass through the Objective-C bridge feel "foreign" to Swift. Underscore-laden parameter names, KotlinInt boxing, generics that erode into their upper bounds — these are the first things a Swift developer sees. Swift export's Alpha status, arriving with Kotlin 2.4.0, aims to eliminate that bridge entirely, but there's still a gap between "aiming to" and "solving." This article walks through, with sources, what Swift export genuinely does today with Kotlin 2.4.0, which limitations are still in place, and what awaits you in the migration.
💡 Pro Tip: Before you bring Swift export into a production project, read the current limitation list on kotlinlang.org/docs/native-swift-export.html — its requirement for "direct integration" puts every existing KMP setup integrated via CocoaPods directly out of scope.Table of Contents
- Why Now: Kotlin 2.4.0 and Swift Export Alpha
- The Concrete Cost of the Obj-C Bridge
- What Swift Export Does and Doesn't Do Today
- What to Watch For When Designing Your Shared Module's API
- The Effect of the CMS GC Default on iOS Smoothness
- How Swift Package Dependencies Get Wired Up
- What Breaks During Migration, and the Version Lock
- Today's Maturity: An Honest Assessment
- FAQ
- What is Kotlin Swift export, and how is it different from the Obj-C bridge?
- What changed on the KMP iOS side in Kotlin 2.4?
- Can Swift packages be used as dependencies in KMP?
- Is Swift export ready for production?
- Should I choose SKIE or Swift export?
- Update (September 2026)
- Conclusion
- Sources
Why Now: Kotlin 2.4.0 and Swift Export Alpha
It's worth pinning down the dates here, because sources mix them up easily. According to the release-history table at kotlinlang.org/docs/releases.html, Kotlin 2.4.0 shipped on June 3, 2026; JetBrains' official announcement (blog.jetbrains.com/kotlin/2026/06/kotlin-2-4-0-released/) summarizes the release as: "Kotlin/Native: Support for Swift packages as dependencies, updates on Swift export, and the CMS GC enabled by default." So Swift export moving to Alpha, Swift packages becoming addable as dependencies, and CMS GC becoming the default — all three belong to 2.4.0.
2.4.10, by contrast, shipped on July 14, 2026, but the release notes describe it plainly: "A bug fix release for Kotlin 2.4.0" — no new features. I'm drawing this distinction deliberately, because some secondary sources covering Swift export conflate the two dates.
The relevant section heading on kotlinlang.org/docs/whatsnew24.html reads "Swift export goes Alpha with improved concurrency support" — Alpha, not Beta. Touchlab.co's analysis (touchlab.co/the-future-of-kmps-ios-interop) summarizes it the same way: "Swift Export just hit alpha, with improved concurrency support." The same article flags an important limitation: cross-language inheritance wasn't supported at that point — even Kotlin classes marked open showed up as final on the Swift side. As you'll see below, this is Swift export's most defining architectural constraint today.
Why does this distinction (Alpha vs. Beta, 2.4.0 vs. 2.4.10) matter so much? Because a sentence like "X landed for Swift export" often circulates without noting which sub-version it actually landed in, and a team making a migration decision can target the wrong version and flip a flag that breaks their build. Make this a reflex: the moment you see a feature's name, verify which version it landed in against the releases.html table before touching gradle.properties. Mistaking a bug-fix release for a feature release is the single most common reason behind a day lost to "it doesn't work for me."
The Concrete Cost of the Obj-C Bridge
The Objective-C bridge has long been the most complained-about layer of KMP-iOS integration. The reason is technical: when Kotlin types get converted into Obj-C headers, some information is lost or represented awkwardly.
- Naming: Kotlin's package structure flattens into underscore-laden prefixes in Obj-C, which makes IDE autocomplete harder to read.
- Boxing: primitives like
IntandBooleanturn into boxed Obj-C types such asKotlinIntandKotlinBooleanin some contexts, which doesn't feel natural on the Swift side. - Generics: because Obj-C's generic support is limited, Kotlin generics lose most of their type information as they cross the bridge.
These are recurring themes in touchlab.co's comparison and kotlinlang.org's Swift export motivation text. What the three items have in common: none is a performance problem. Your app doesn't slow down because of the Obj-C bridge; its API surface on the Swift side just becomes harder to read.
The cost accumulates in the minutes every new iOS developer spends asking what KotlinInt is, and in every code review's "why is this underscore here" discussion. Swift export already addresses two of these three today: for naming, the docs list "Kotlin packages are explicitly preserved during export" and "Flattened package structure"; for boxing, it removes the KotlinInt wrapper, saying "Swift export converts nullability information directly." The one unresolved item is generics: "type-erased to their upper bounds." Today's actual blocker for production isn't these three items — it's the Alpha constraints: final-class-only export, direct-integration-only setups, and no IDE migration tool.
What Swift Export Does and Doesn't Do Today
Swift export is the Kotlin/Native compiler generating idiomatic Swift APIs directly from Kotlin code — it disables the Obj-C header bridge entirely (kotlinlang.org/docs/native-swift-export.html). The current limitation list on the native-swift-export.html page can be summarized as follows:
Area | Status in Alpha today |
|---|---|
Supported classes | Only final classes deriving from Any |
Generics | Type erasure still applies, no full generic export |
Collection inheritance | Custom types implementing List/Set/Map are out of scope |
Concurrency | suspend functions map to Swift async, Flow maps to AsyncSequence |
Integration type | Only works in projects set up with "direct integration" |
IDE support | No automatic migration tool, manual migration required |
This table summarizes the items on kotlinlang.org/docs/whatsnew24.html and kotlinlang.org/docs/native-swift-export.html. Structured concurrency support (suspend→async, Flow→AsyncSequence) is the most mature part of Alpha; the "final classes only" restriction, by contrast, creates direct friction for the shared architectures most existing KMP projects build with open classes.
1// commonMain — Swift export can export this class because it's final2class UserRepository(private val api: ApiClient) {3 suspend fun fetchUser(id: String): User = api.getUser(id)4}5 6// But this class CANNOT be exported in Alpha (requires open + inheritance)7open class BaseViewModel {8 open fun onAppear() {}9}The generated code on the Swift side turns a suspend fun directly into an async function — a genuine gain compared to the same function turning into a callback-based signature in the old Obj-C bridge. Calling this API on the Swift side is possible directly through native async/await syntax, without the boxing layer of the traditional Obj-C bridge:
1// Calling the API generated by Swift export2let repository = UserRepository(api: ApiClient())3let user = try await repository.fetchUser(id: "42")In the old bridge, the same call would have required boxed types like KotlinInt and a completion-handler signature; here it maps one-to-one onto Swift's own concurrency model. This directly affects readability, especially in apps that chain calls inside Task {} blocks — the code no longer "obviously came from Kotlin," it "looks like it was written in Swift."
What to Watch For When Designing Your Shared Module's API
There are practical takeaways you can draw directly from Swift export's current limitations (final-class-only export, generic type erasure); read these not as "official rules" but as recommendations that follow from the limitation list in the docs:
- Composition over inheritance: designing the public APIs you'll export in
commonMainwithfinalclasses plus interface composition, instead ofopenclasses, improves compatibility with Swift export. - Keep generics away from the API boundary: even when public functions with generic parameters do get exported, their type information can weaken on the Swift side; it's safer to keep generics in internal layers and present the outer surface with concrete types.
- Prefer suspend functions: using
suspend funinstead of callback-based APIs lets you directly benefit from the concurrency mapping, which is Swift export's most mature strength.
1// Recommended: final + suspend + concrete return type2class ProfileService(private val client: ApiClient) {3 suspend fun loadProfile(userId: String): Profile =4 client.get("profile/$userId")5}The Effect of the CMS GC Default on iOS Smoothness
With Kotlin 2.4.0, CMS (Concurrent Mark & Sweep) GC became the default memory manager in Kotlin/Native (blog.jetbrains.com/kotlin/2026/06). The docs spell out the rollback path explicitly: "If you face problems, you can switch back to PMCS" — just add the kotlin.native.binary.gc=pmcs binary option to gradle.properties (kotlinlang.org/docs/whatsnew24.html). Note the detail: this isn't a compiler flag, it's a binary option defined inside gradle.properties; adding it to the Gradle command line won't have the effect you expect.
The practical upshot: this default change can silently alter your app's GC profile in a version you upgrade to without ever measuring memory behavior. When you upgrade to 2.4.0, re-measure memory usage and stutter complaints on iOS; if you see a regression, repeat the same measurement with pmcs for a clear comparison point. The docs give no project-specific number, but don't call it negligible either: for CMS they literally say "This significantly improves GC pause duration and app responsiveness" — the actual difference in your project still isn't knowable without measuring it.
1# gradle.properties — if you want to fall back from CMS to the old PMCS2kotlin.native.binary.gc=pmcsHow Swift Package Dependencies Get Wired Up
The "Swift package import" feature, available since Kotlin 2.4.0, lets a KMP module define a SwiftPM dependency directly in its Gradle config (kotlinlang.org/docs/whatsnew24.html, "Swift package import" section). Detailed setup steps: kotlinlang.org/docs/multiplatform/multiplatform-spm-import.html.
Dependencies are written into the swiftPMDependencies {} block in the same build.gradle.kts file where the Apple targets are declared:
1// build.gradle.kts2plugins {3 kotlin("multiplatform") version "2.4.0"4}5 6kotlin {7 iosArm64()8 iosSimulatorArm64()9 10 swiftPMDependencies {11 swiftPackage(12 url = url("https://github.com/firebase/firebase-ios-sdk.git"),13 version = from("12.11.0"),14 products = listOf(15 product("FirebaseAI"),16 product("FirebaseAnalytics"),17 ),18 )19 }20}Notice the three parts of the syntax: url() gives the package's Git address, version the resolution rule (like from("12.11.0")), and products the list of products you want to access from Kotlin code. Clang module discovery is automatic by default ("automatically discovers Clang modules"), and you turn it off with discoverClangModulesImplicitly = false; to pin a transitive dependency's version, add it separately, with its own swiftPackage(...) call and an empty products list.
The most concrete practical benefit here is direct access to the SwiftPM ecosystem (analytics or UI libraries, say) with no CocoaPods history required. If your current setup is on CocoaPods, the path isn't closed: the docs address this scenario — "If your project relies on CocoaPods dependencies, you can migrate the current setup to use Swift packages" — and say the KMP tooling helps automatically reconfigure the project during migration (kotlinlang.org/docs/whatsnew24.html). This connects directly to Swift export's "direct integration" requirement too: moving off CocoaPods opens the door to both features at once.
What Breaks During Migration, and the Version Lock
This is the riskiest part, since migrating to Swift export means building on an unstable/Alpha API. Three items to watch:
Item | Why it matters |
|---|---|
Minimum platform targets | Default minimum versions rose: iOS/tvOS 14→15, macOS 11→12, watchOS 7→8 — supporting anything lower requires freeCompilerArgs |
"Direct integration" requirement | Swift export only works with this setup type; classic CocoaPods-based setups are out of scope |
Absence of an IDE migration tool | Migration is manual, no automatic converter is offered yet |
These are the most commonly overlooked points — especially the minimum platform version increase, which forces a direct product decision if your user base skews toward older devices.
These items are worth unpacking. The minimum version increase means it's risky to decide without checking the percentage of users on older OS versions in your analytics dashboard — this varies enormously by product and market, so look at your own user base's distribution rather than a generic threshold. "Direct integration" means moving from CocoaPods to an XCFramework or SwiftPM; that migration is a separate engineering effort, planned independently of Swift export. The absence of an IDE migration tool is the most underrated risk: manually moving a large public API surface is error-prone and time-consuming — so start with a small sandbox module containing only a few final classes and suspend functions.
Today's Maturity: An Honest Assessment
The most in-depth technical piece comparing Swift export with SKIE (Touchlab's product) was published on touchlab.co (touchlab.co/the-future-of-kmps-ios-interop), showing compiled Obj-C/Swift code output side by side. The same article says Swift export "does not yet support cross-language inheritance" — SKIE still offers broader coverage for an architecture that requires inheritance. Don't forget comparisons like this are dated: an Alpha feature's scope can expand with every release, so check which Kotlin version the comparison was written against.
Other English-language sources fall into two clusters: release-summary articles (Medium-style, shallow list format) and practical setup guides (carrion.dev, dating from the 2.1.0 era, no 2.4.x update). In short: Swift export is real and progressing, but it's still too early to call it "production-ready" — the Alpha label isn't there for nothing. The final-class-only restriction, the absent IDE migration tool, and the "direct integration" requirement are a direct migration blocker for most existing KMP projects today.
One more thing: JetBrains' own documentation nowhere describes Swift export as "production-ready" — the status is explicitly Alpha. That's not an official commitment but an actively developing feature; the API surface can change version to version. The cost of adopting this as a team is re-checking the limitation list with every major release and reviewing the source diff of your exported APIs. Bringing Swift export into your main codebase without accepting that cost can create a recurring maintenance burden over the next several releases.
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
Everything you should check before a Swift export migration, in one list — a checklist you can follow in order, from the sandbox trial through to production.
FAQ
What is Kotlin Swift export, and how is it different from the Obj-C bridge?
Swift export lets the Kotlin/Native compiler generate idiomatic Swift APIs directly from Kotlin code; it disables the Objective-C header bridge (boxed types, flattened package naming) (kotlinlang.org/docs/native-swift-export.html). Generics lose information in the Obj-C bridge, while Swift export's code uses native Swift types — though the final-class-only restriction still persists as of 2.4.0.
What changed on the KMP iOS side in Kotlin 2.4?
With Kotlin 2.4.0 (June 3, 2026), Swift export moved to Alpha, improved concurrency support arrived, support for adding Swift packages as Gradle dependencies opened up, and CMS became the default in the Kotlin/Native GC (blog.jetbrains.com/kotlin/2026/06). 2.4.10 (July 14, 2026) is only a bug-fix release; it brings nothing new to the iOS interop side.
Can Swift packages be used as dependencies in KMP?
Yes — since 2.4.0, "Swift package import" lets a KMP module define a SwiftPM dependency in its Gradle config (kotlinlang.org/docs/whatsnew24.html). Guide: kotlinlang.org/docs/multiplatform/multiplatform-spm-import.html.
Is Swift export ready for production?
No, still Alpha as of Kotlin 2.4.0. The final-class-only restriction, generic type erasure, and the missing IDE migration tool make a direct migration difficult for most existing projects. A small sandbox module is a safer starting point.
Should I choose SKIE or Swift export?
Touchlab.co's comparison (as written) shows SKIE is more mature with broader coverage, including inheritance; Swift export is the direction to prefer long-term as the official JetBrains solution. For now, it's reasonable to try both at small scale and decide based on your project's needs.
Update (September 2026)
When this article was first written (July 28, 2026), only Kotlin 2.4.0's Alpha opening applied. Kotlin 2.4.20, released on September 7, 2026, brought three advances that directly target the final-class-only restriction I mentioned above (kotlinlang.org/docs/whatsnew2420.html):
- Cross-language inheritance: the docs say, verbatim: "Kotlin 2.4.20 introduces cross-language inheritance support in Swift export." A typical use case, per the same page: "A common use case for this feature is the reverse import pattern, where you define a contract in Kotlin and provide platform-specific implementations on the Swift side." The "even open classes are final" limitation touchlab.co criticized is now partially addressed.
- Sealed class/interface export: the same page: "Kotlin 2.4.20 adds support for sealed classes and interfaces to Swift export." Sealed hierarchies in Kotlin map onto Swift enums; with full Xcode autocomplete, consumers can write exhaustive
switchstatements without adefaultcase. - Automatic Package.swift generation: when exporting an XCFramework that depends on SwiftPM packages, the
assembleSharedXCFrameworkGradle task now generates aPackage.swiftfile to ship alongside the XCFramework.
In short: 2.4.0 opened Alpha, 2.4.20 gave it substance. If you evaluated this in July and found it too early, it's worth another look in September 2026 — the cross-language inheritance progress alone partially removes one of the biggest architectural blockers.
Conclusion
With Kotlin 2.4.0, Swift export moved to Alpha and solved two of the three most complained-about problems with the Obj-C bridge — naming and primitive boxing — leaving generic type erasure unresolved. The real blocker for production is the Alpha constraints: final-class-only export, direct-integration-only, and no IDE migration tool. Before revisiting your architecture, add silent breaking changes like the minimum platform version increase to your checklist, and decide by trying it in a small sandbox module; follow the release notes from the pre-releases onward, because an Alpha API's scope can shift with every version.
For more: Kotlin Multiplatform 1.1 Stable Production Case Study shows how the shared architecture behaves in production; Kotlin 2.1: K2 Compiler and Context Parameters covers the previous step in this release family; Compose Multiplatform: Android + iOS Production Deployment completes the parallel UI-layer story; Flutter vs SwiftUI: 3 Years + 60K LOC Production Comparison frames the cross-platform decision more broadly; The Complete Swift 6.0 Guide reinforces the concurrency foundation on the Swift side.
Sources
- Kotlin Releases — official release table — primary source for the 2.4.0 (June 3, 2026) and 2.4.10/2.4.20 dates.
- What's New in Kotlin 2.4.0 — Swift export Alpha,
swiftPMDependenciessyntax, the CMS GC default, and thekotlin.native.binary.gc=pmcsrollback. - What's New in Kotlin 2.4.20 — primary source for cross-language inheritance, sealed class/interface export, and
Package.swiftgeneration. - Kotlin Swift Export Documentation — current limitation list (final-class-only, generic erasure, direct integration requirement).
- Kotlin 2.4.0 Released — JetBrains Blog — official announcement, ties all three features to 2.4.0.
- The Future of KMP's iOS Interop — Touchlab — technical comparison of Swift export with SKIE, and the concrete problems of the Obj-C bridge.
- Swift Export Setup Guide — carrion.dev — practical walkthrough of the gradle.properties flag and the embedSwiftExportForXcode step.
- Multiplatform SwiftPM Import Guide — detailed steps for setting up a Swift package dependency.
Tags
iOS Development News
Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.
We respect your privacy. You can unsubscribe at any time.

