kmp-boundaries
Kotlin Multiplatform Boundary Design
Core rules for any KMP boundary:
- Keep
commonMainsemantic — describe what the product needs, not Android/iOS mechanics:currentRegion(), nevercurrentRegionFromAndroidLocale(context). - Split by capability —
Clipboard,ShareSheet,Haptics,Biometricsas separate interfaces, not onePlatformgod object. - Keep actuals thin — they translate, they don't decide; a business
if/wheninside an actual belongs in common, tested with a fake. - Prefer a common
interface+ per-platform binding overexpect classwhenever you need fakes / DI / lifecycle / runtime selection. - Introduce an intermediate source set (
skikoMain,appleMain) only when two platforms genuinely share an actual.
Two boundaries get the most detail below: the Activity-owned platform-UI boundary, and the AGP-9 KMP-library constraints.
Related: android-skills:kmp-ktor (network boundary), compose/references/multiplatform.md (Compose-MP mechanics), android-skills:kotlin-coroutines (scope ownership). For the iOS↔Swift bridge — Kotlin→Swift naming, type widths (Int is 32-bit), SKIE suspend→async / Flow→AsyncSequence, sealed-class exhaustiveness, SwiftUI embedding — load references/ios-interop.md when authoring the iOS-side actual.
Platform-UI bindings are Activity-owned, not Context-owned
The single most common Android boundary mistake: passing applicationContext / LocalContext.current into a binding that actually needs an Activity, then papering over the lifecycle gap with Intent.FLAG_ACTIVITY_NEW_TASK. That flag is a smell — it hides that this is a foreground-UI operation. Hold an Activity instead.