Consuming Shared Code from Each App
The shared module is just a dependency
To both apps, the shared module looks like a normal library — but the way each consumes it differs slightly. Understanding this removes the “magic” and makes KMP feel approachable.
Android: a Gradle dependency
This is the easy side. The Android app adds the shared module in Gradle and calls its Kotlin classes directly — same language, no bridging.
// androidApp/build.gradle.kts
dependencies { implementation(project(":shared")) }
// in a Compose screen / Android ViewModel
val repo = UserRepository(/* injected */)
val users = repo.getUsers() // just Kotlin calling Kotlin
iOS: a native framework
For iOS, Gradle compiles the shared module (via Kotlin/Native) into a framework that Xcode links. In Swift you import it and call its types like any Swift library.
import shared // the generated framework
let repo = UserRepository(api: api, db: db)
// suspend functions appear as async (with SKIE) or completion handlers
let users = try await repo.getUsers()
How Kotlin maps to Swift
- Kotlin classes & data classes → Swift classes.
- Kotlin
suspend→ Swiftasync(or a completion handler). - Kotlin
Flow→ SwiftAsyncSequence(with SKIE) or a wrapper. - Sealed classes → Swift enums (with SKIE) for clean
switchhandling.
Tools like SKIE dramatically improve this mapping, making the shared API feel like idiomatic Swift.
The typical build flow
- You change Kotlin in the
sharedmodule. - Android picks it up directly on the next build.
- For iOS, Gradle rebuilds the framework (often automated via a build phase in Xcode), and Swift sees the updated API.
What iOS developers need to know
They don’t need to write Kotlin — they consume the shared API like a library, focusing on building a great SwiftUI experience on top. A little familiarity with how Kotlin maps to Swift (above) is enough.
Common mistakes
- Expecting Swift to use
Flow/suspendwith no bridging — add SKIE or wrappers. - Forgetting to rebuild the iOS framework after changing shared code.
- Exposing overly “Kotlin-ish” APIs that feel awkward in Swift — design the shared surface with iOS consumers in mind.
Summary: Android uses the shared module as a plain Gradle dependency (Kotlin calling Kotlin); iOS links it as a native framework imported into Swift. Use SKIE to mapsuspend/Flow/sealed classes cleanly, and design the shared API to feel natural on both sides.