Kotlin to Swift Export: Native iOS Integration Guide 2025
Kotlin 2.2.20's Swift Export delivers native Swift interop for KMP. Code examples, migration guide, and production readiness assessment for iOS teams.
Posted by
Related reading
Kotlin Multiplatform Testing in 2025: Complete Guide to Unit, Integration & UI Tests
Master KMP testing with this complete guide. Learn to write shared tests once, run everywhere. Includes Kotest, Turbine, and MockK examples with real production patterns.
How to Set Up Kotlin Multiplatform: Complete Development Guide 2025
Learn how to set up Kotlin Multiplatform for cross-platform mobile development. Complete step-by-step guide covering environment setup, Firebase integration, and production-ready configuration in 2025.
How to Build a Production-Ready Mobile App with Kotlin Multiplatform (in Days)
Looking to launch Android and iOS apps fast? Learn how to build a production-ready mobile app using Kotlin Multiplatform: with auth, payments, CI/CD, and more, all from a single codebase.

TL;DR: Why Swift Export is a game-changer for KMP
- Swift developers call KMP code that looks and feels like native Swift
- Better IDE autocomplete and type inference in Xcode
- Preserved nullability, packages, and type aliases
- Multi-module support with clean separation
The Objective-C bridge problem that's been haunting iOS developers
What iOS developers struggle with today
Any types.Int? require boxing with KotlinInt, making Swift code verbose and un-idiomatic.What is Swift Export? The direct Kotlin-to-Swift bridge
Kotlin Code → Objective-C Headers → Swift Code (painful)
Kotlin Code → Native Swift Modules (seamless)
Key improvements with Swift Export
✅ Multi-module support
swift// Clean, modular imports import SharedNetworking import SharedDatabase import SharedAuth // Instead of everything coming from one giant "Shared" module
✅ Package preservation
kotlin// Kotlin package com.myapp.auth class LoginManager package com.myapp.network class LoginManager // Different class, same name
swift// Swift - No naming conflicts! import com.myapp.auth.LoginManager import com.myapp.network.LoginManager
✅ Type aliases preserved
kotlin// Kotlin typealias UserId = String typealias ProductId = Long fun getUser(id: UserId): User
swift// Swift - Type aliases work! typealias UserId = String typealias ProductId = Int64 func getUser(id: UserId) -> User
✅ Enhanced nullability
Int?.kotlin// Kotlin fun calculateDiscount(code: String?): Int?
swift// With Objective-C bridge (OLD WAY): func calculateDiscount(code: String?) -> KotlinInt? // Awkward! // With Swift Export (NEW WAY): func calculateDiscount(code: String?) -> Int? // Natural!
✅ No ambiguity with overloaded functions
kotlin// Kotlin fun fetchData(id: String): Data fun fetchData(id: String, useCache: Boolean): Data
swift// Swift - Both overloads work correctly let data1 = fetchData(id: "123") let data2 = fetchData(id: "123", useCache: true)
How to set up Swift Export in your KMP project
Step 1: Update to Kotlin 2.2.20+
gradle/libs.versions.toml:toml[versions] kotlin = "2.2.20"
Step 2: Enable Swift Export
gradle.properties:propertieskotlin.experimental.swift-export.enabled=true
Step 3: Configure Swift Export in build.gradle.kts
kotlinimport org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework import org.jetbrains.kotlin.gradle.ExperimentalSwiftExportDsl plugins { alias(libs.plugins.kotlinMultiplatform) } kotlin { // iOS targets iosX64() iosArm64() iosSimulatorArm64() @OptIn(ExperimentalSwiftExportDsl::class) swiftExport { // Root module name moduleName = "Shared" // Flatten packages to avoid deeply nested imports flattenPackage = "com.myapp.shared" // Export additional modules export(project(":networking")) { moduleName = "SharedNetworking" flattenPackage = "com.myapp.networking" } export(project(":database")) { moduleName = "SharedDatabase" flattenPackage = "com.myapp.database" } } sourceSets { commonMain.dependencies { // Your common dependencies implementation(libs.kotlinx.coroutines.core) } iosMain.dependencies { // iOS-specific dependencies } } }
Step 4: Update your Xcode build phase
embedAndSignAppleFrameworkForXcode task with the new embedSwiftExportForXcode task:bash# OLD (Objective-C bridge): ./gradlew :shared:embedAndSignAppleFrameworkForXcode # NEW (Swift Export): ./gradlew :shared:embedSwiftExportForXcode
Step 5: Build and use in Swift
swiftimport Shared import SharedNetworking import SharedDatabase class MyViewController: UIViewController { let authManager = AuthManager() let apiClient = ApiClient() func login(email: String, password: String) { // Calling Kotlin code feels like native Swift! authManager.login(email: email, password: password) { result in switch result { case .success(let user): print("Logged in: \(user.name)") case .failure(let error): print("Login failed: \(error)") } } } }
Real-world example: Before and after Swift Export
Kotlin shared code
kotlin// shared/src/commonMain/kotlin/com/myapp/auth/AuthRepository.kt package com.myapp.auth typealias UserId = String data class User( val id: UserId, val name: String, val email: String, val isPremium: Boolean ) class AuthRepository { suspend fun login(email: String, password: String): Result<User> { // Simulate API call delay(1000) return if (email.isNotEmpty() && password.length >= 6) { Result.success( User( id = "user_123", name = "John Doe", email = email, isPremium = false ) ) } else { Result.failure(Exception("Invalid credentials")) } } fun logout(userId: UserId?) { println("Logging out user: $userId") } fun getDiscount(isPremium: Boolean?): Int? { return when (isPremium) { true -> 20 false -> 5 null -> null } } }
Using it in Swift: OLD WAY (Objective-C bridge)
swift// ❌ Painful: Objective-C bridge version import shared class LoginViewModel { let authRepo = SharedAuthRepository() func login(email: String, password: String) { // Awkward: Calling suspend functions requires ceremony authRepo.login(email: email, password: password) { result, error in if let result = result { // Type casting required if let user = result as? User { print("User: \(user.name)") } } } } func logout(userId: String?) { // Verbose: Optional primitives require KotlinInt wrapping if let userId = userId { authRepo.logout(userId: userId) } else { authRepo.logout(userId: nil) } } func getDiscount(isPremium: Bool?) { // Awkward: Nullable Bool becomes KotlinBoolean let kotlinBool = isPremium != nil ? KotlinBoolean(value: isPremium!) : nil let discount = authRepo.getDiscount(isPremium: kotlinBool) // Result is KotlinInt?, not Int? if let kotlinDiscount = discount { let intDiscount = kotlinDiscount.intValue // Need to unwrap print("Discount: \(intDiscount)%") } } }
Using it in Swift: NEW WAY (Swift Export)
swift// ✅ Beautiful: Swift Export version import Shared import com.myapp.auth // Package preserved! class LoginViewModel { let authRepo = AuthRepository() func login(email: String, password: String) async { // Clean: async/await works naturally do { let user = try await authRepo.login(email: email, password: password) print("User: \(user.name)") print("Premium: \(user.isPremium)") } catch { print("Login failed: \(error)") } } func logout(userId: UserId?) { // Natural: Optional String just works authRepo.logout(userId: userId) } func getDiscount(isPremium: Bool?) { // Clean: Native Swift optionals if let discount = authRepo.getDiscount(isPremium: isPremium) { print("Discount: \(discount)%") // Native Int, not KotlinInt } } }
What works (and what doesn't) in Swift Export 2025
✅ What works well right now
- Basic types and nullability - Strings, primitives, nullable types all map cleanly
- Data classes - Kotlin data classes become Swift structs
- Functions and methods - Including overloads and default parameters
- Packages and modules - Multi-module projects with preserved structure
- Type aliases - Your type aliases work in Swift
- Enums - Kotlin enums map to Swift enums (not integers!)
- Basic coroutines support - Suspend functions can be called from Swift
⚠️ What's coming but not ready yet
- Full suspend function support - Currently works but will be enhanced with better async/await integration
- Kotlin Flow to Swift Combine/AsyncSequence - Planned for future releases
- Advanced generics - Some complex generic scenarios still need work
- Codable conformance - Automatic Codable generation for Kotlin classes (highly requested)
- Full Swift Package Manager integration - Currently works with direct integration only
🚫 Current limitations to be aware of
- Experimental status - APIs and behavior may change between releases
- Direct integration only - Works with direct Xcode integration, not yet with CocoaPods/Carthage
- Documentation evolving - Official docs are improving but not comprehensive yet
- Limited third-party tooling - Tools like SKIE don't yet support Swift Export
Migration guide: From Objective-C bridge to Swift Export
Migration strategy: Gradual is better
Phase 1: Set up Swift Export in parallel (Week 1)
- Update to Kotlin 2.2.20+
- Enable Swift Export in gradle.properties
- Configure swiftExport DSL in build.gradle.kts
- Keep your existing Objective-C bridge working
Phase 2: Test with a single module (Week 2)
- Choose a low-risk module (like a utility library)
- Export it with Swift Export
- Update Swift code to use the new module
- Test thoroughly before proceeding
Phase 3: Migrate remaining modules (Weeks 3-4)
- Migrate one module at a time
- Update Swift imports and usage
- Remove Objective-C bridge dependencies as you go
Phase 4: Remove Objective-C bridge (Week 5)
- Once all modules are migrated, remove the old framework task
- Clean up build scripts and configurations
- Update CI/CD pipelines
Common migration issues and solutions
Issue: "Module not found" in Xcode
embedSwiftExportForXcode instead of embedAndSignAppleFrameworkForXcode.Issue: Type mismatches after migration
swift// Before: let count: KotlinInt = repository.getCount() // After: let count: Int = repository.getCount()
Issue: Suspend functions not working
swift// Before (Objective-C bridge): repository.fetchData { result, error in // Handle result } // After (Swift Export): Task { do { let result = try await repository.fetchData() // Handle result } catch { // Handle error } }
Issue: Build errors with CocoaPods/Carthage
Should you use Swift Export in production today?
✅ Use Swift Export if...
⚠️ Wait for stable release if...
The pragmatic approach: Test it now, adopt when stable
- Experiment with Swift Export in a side project or feature branch - Get familiar with the setup and benefits
- Monitor JetBrains' roadmap updates - Track progress toward the stable release in 2026
- Prepare your codebase - Structure your Kotlin code to be Swift-friendly (avoid problematic patterns)
- Plan migration - Have a migration strategy ready for when stable release arrives
- Adopt gradually - When you do migrate, start with low-risk modules
The future of KMP iOS development: What's coming next
2025-2026 Roadmap highlights
Built-in suspend function support
kotlin// Kotlin suspend fun fetchUser(id: String): User
swift// Swift - Works seamlessly with async/await let user = try await fetchUser(id: "123")
Kotlin Flow to Swift AsyncSequence
kotlin// Kotlin fun observeUsers(): Flow<List<User>>
swift// Swift - Native for-await loop for await users in observeUsers() { print("Users updated: \(users.count)") }
Codable conformance generation
kotlin// Kotlin @Serializable data class User(val id: String, val name: String)
swift// Swift - Automatically Codable! let json = try JSONEncoder().encode(user) let decoded = try JSONDecoder().decode(User.self, from: json)
Swift Package Manager integration
swift// Package.swift dependencies: [ .package(url: "https://github.com/mycompany/kmp-sdk", from: "1.0.0") ]
Stable release in 2026
Why this matters for iOS adoption
- No more "it feels foreign" objections - Swift Export makes KMP code feel native
- Better iOS team morale - Developers enjoy working with clean, idiomatic Swift APIs
- Easier hiring - iOS developers don't need to learn Objective-C quirks
- Faster adoption - Less friction means faster time-to-value for KMP
Beyond the bridge: Alternative interop solutions
SKIE: Enhanced Objective-C interop
- Automatic suspend function to async/await conversion
- Flow to AsyncSequence bridging
- Better enum support
- Default parameters in Swift
KMMBridge: Simplified iOS distribution
Wrapper classes: Manual Swift-friendly API
swift// Swift wrapper around KMP code class UserRepository { private let kmpRepo: KmpUserRepository init() { self.kmpRepo = KmpUserRepository() } func fetchUser(id: String) async throws -> User { return try await withCheckedThrowingContinuation { continuation in kmpRepo.fetchUser(id: id) { result, error in if let error = error { continuation.resume(throwing: error) } else if let result = result { continuation.resume(returning: result) } } } } }
Getting started with Swift Export today
Your Swift Export getting started checklist
- Create a sample project
- Start with the official Swift Export sample: github.com/Kotlin/swift-export-sample
- Explore the three-module example to understand the DSL configuration
- Update your existing project
- Upgrade to Kotlin 2.2.20 or later
- Enable Swift Export in gradle.properties
- Configure the swiftExport DSL in build.gradle.kts
- Test with a low-risk module
- Choose a utility or helper module
- Export it and verify in Xcode
- Write Swift code that uses it
- Monitor the roadmap
- Follow JetBrains' Kotlin blog for updates: blog.jetbrains.com/kotlin
- Track the official roadmap: kotlinlang.org/docs/roadmap.html
- Join the community
- Kotlin Slack: kotlinlang.slack.com (#multiplatform channel)
- Report issues: youtrack.jetbrains.com/issues/KT
- Share feedback to help shape the final API
Swift Export vs Objective-C bridge: The final verdict
✅ Use Swift Export when:
- Starting a new KMP project
- Your iOS team values developer experience
- You use direct framework integration
- You're comfortable with experimental features
- You want to prepare for the future of KMP
- You can dedicate time to potential debugging
- Your project uses modern Swift concurrency
⚠️ Stick with Objective-C bridge when:
- You need guaranteed API stability
- You rely on CocoaPods/Carthage
- You use third-party tools like SKIE (for now)
- You're in a regulated industry
- Your project is already working well
- You can wait for the 2026 stable release
- Migration risk outweighs benefits currently
Ready to build production-ready KMP apps with great iOS support?
KMPShip: Production-ready Kotlin Multiplatform starter kit
✨ iOS-first developer experience:
- 🔐 Authentication (Google, Apple, Email)
- 💳 In-app purchases (StoreKit integration)
- 🔔 Push notifications (APNs ready)
- 🚀 CI/CD with Fastlane
💪 Built for Swift developers:
- 📱 Swift-friendly API design
- 📚 iOS-specific documentation
- 🏗️ Clean architecture patterns
- ⚡ Ready for Swift Export migration
Sources and references
Official documentation and announcements:
- Interoperability with Swift using Swift export - Official Kotlin Documentation
- What's new in Kotlin 2.2.20 - Kotlin Documentation
- Kotlin Multiplatform Development Roadmap for 2025 - JetBrains Blog
- What's Next for Kotlin Multiplatform and Compose Multiplatform – August 2025 Update - JetBrains Blog
- Swift Export Sample Project - Official GitHub Repository
Technical guides and tutorials:
- Swift Export: Natural Interoperability between Kotlin and Swift - Santiago Mattiauda
- Swift export in KMP - Carrion.dev
- Interoperability with Swift/Objective-C - Kotlin Documentation
- Kotlin Swift Interopedia - Hands-on GitHub Repository
- Kotlin Multiplatform and interoperability with Swift/Objective-C - Droidcon
Community insights and best practices:
- Kotlin 2.2.20: The Features That'll Change Your Code (KMP Swift Export, webMain, and More) - Sunil Kumar
- Integrating Native Swift Code in a Kotlin Compose Multiplatform App - Droidcon
- Yes, Calling Swift from Kotlin Multiplatform is Easy: No Plugins, No Magic, Just CInterop - Mathieu Perroud
- Using Kotlin Multiplatform With KMMBridge and SKIE To Publish A Native Swift SDK - PowerSync
Continue your Kotlin Multiplatform journey
- Compose Multiplatform for iOS Stable in 2025: What Developers Need to Know
- Big Tech's Secret Weapon: How Netflix, McDonald's & Cash App Ship Faster with Kotlin Multiplatform
- How to Set Up Kotlin Multiplatform: Complete Development Guide 2025
- Kotlin Multiplatform vs Flutter vs React Native: The 2025 Developer Guide
Quick Questions About KMP Development
- Do I need a Mac for iOS development? - Platform requirements explained
- How long does setup actually take? - Real timeline expectations
- What's the difference between KMP and other frameworks? - Framework comparison