Use when designing, implementing, or reviewing platform-specific integrations in KMP projects, including source-set placement, hierarchical sharing, expect/actual usage, platform API access, and shared-to-native abstraction boundaries.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Use when designing, implementing, or reviewing platform-specific integrations in KMP projects, including source-set placement, hierarchical sharing, expect/actual usage, platform API access, and shared-to-native abstraction boundaries.
license
Apache-2.0
metadata
{"author":"Mariano Miani","version":"1.2.0"}
Kotlin Multiplatform Platform Bridges
Use this skill when designing, implementing, or reviewing platform-specific integrations and shared-to-native boundaries in a Kotlin Multiplatform project.
This skill is intentionally strict. Its purpose is to keep platform-specific code at the edges, maximize valid sharing through source-set hierarchy, avoid unnecessary expect/actual, and preserve testable abstractions between shared and native code.
Primary goals
The bridge design should optimize for:
correct source-set placement
maximum valid code sharing before introducing platform splits
clear shared-to-native abstraction boundaries
minimal and justified expect/actual usage
platform API access through the least-coupled mechanism
reuse across similar platforms through intermediate source sets
testability and replaceability of native integrations
avoidance of platform/vendor types leaking into shared business logic
Do not default to / for every native dependency.
Start from the least specialized mechanism that solves the problem cleanly.
expect
actual
Official defaults to prefer
Unless the project has a strong reason not to, prefer these defaults:
share code in commonMain when it is valid for all declared targets
share code among similar platforms through hierarchical source sets before duplicating implementations
use the default hierarchy template unless the project truly requires custom hierarchy wiring
check for existing multiplatform libraries before writing platform bridges
prefer regular interfaces and injected implementations when they model the dependency well
use expect/actual mainly for narrow platform-specific access points
keep actual implementations in intermediate source sets like iosMain when one implementation is valid for several platform targets
keep platform-specific registration, lifecycle, packaging, and SDK wiring outside shared business logic
Review dimensions
1. Sharing-first source-set placement
Check whether code is placed in the highest valid shared source set before splitting into platform code.
Prefer:
commonMain for logic valid for all targets
intermediate shared source sets for code valid for some but not all targets
platform source sets only when APIs or behavior genuinely differ
Flag as a concern when:
code is duplicated in platform source sets even though it could live in shared code
platform splitting happens too early
commonMain is underused because the design assumes platform differences before validating them
actual implementations are placed at the right hierarchy level
Flag as a concern when:
expect declarations include implementation
packages differ
one target is missing an actual
actual code is duplicated in concrete targets when an intermediate source set would suffice
7. Avoid overusing expect/actual classes
Kotlin explicitly recommends relying on standard language constructs wherever possible. The expect/actual mechanism overall is stable, but expect/actualclasses (non-annotation expect class declarations) carry restrictions: in Kotlin 2.0+, non-annotation expect classes must have a corresponding actual class (not a typealias) in each target, and their member declarations must match. This makes expect/actual classes heavier to maintain than expect functions or properties. Always check the current Kotlin docs for the latest stability status of this feature. (https://kotlinlang.org/docs/multiplatform/multiplatform-expect-actual.html)(https://kotlinlang.org/docs/multiplatform/multiplatform-expect-actual.html)
Check whether:
interfaces, functions, properties, or factories would be enough
expect/actual classes are used only when truly justified
the team understands the tradeoff of choosing a Beta language feature
Flag as a concern when:
simple abstractions are modeled as expect classes without need
the design unnecessarily restricts each target to one implementation
fakes and alternative implementations become harder because of class-based actualization
8. Shared contract quality
Shared code should define what the app needs, not how each platform implements it.
Check whether:
shared contracts are small and purposeful
common abstractions hide platform/vendor details
bridge surfaces are stable enough for callers
shared contracts model capabilities, not SDK quirks
Kotlin documents passing platform implementations from platform entry points as a valid alternative to expect/actual. In practice, this means: at the platform main function or app startup (e.g. MainActivity.onCreate() on Android, the app entry point on iOS), the platform constructs concrete implementations of shared interfaces and passes them into shared code — typically via constructor injection, factory functions, or a DI container. Shared code receives an already-constructed dependency and does not need to know which platform provided it. (https://kotlinlang.org/docs/multiplatform/multiplatform-connect-to-apis.html)
What "entry-point wiring" means concretely: the platform's main entry point (e.g., Android Application.onCreate(), iOS main.kt, desktop main()) constructs platform-specific implementations and injects them into shared code — typically through a constructor, factory function, or DI graph — rather than having shared code create or locate them itself. Shared code depends on an interface or abstract type; only the entry point knows the concrete class.
Check whether:
platform-specific instantiation happens at platform entry points when appropriate
shared code receives already-constructed dependencies instead of owning platform bootstrapping
platform startup remains thin and explicit
different platforms can supply different implementations of the same interface without touching shared code