
Kotlin Multiplatform
Decide which Amethyst-style expect/actual abstractions belong in shared KMP code versus platform code when shipping a cross-platform client.
Install
npx skills add https://github.com/vitorpamplona/amethyst --skill kotlin-multiplatformWhat is this skill?
- Documents real Amethyst quartz patterns: Secp256k1Instance expect/actual for Schnorr sign and verify
- Explains Log expect object abstraction across Android util.Log, JVM logging, and iOS NSLog/OSLog
- States rationale: always abstract crypto when platform security APIs differ and protocol requires signatures
- Maps implementations: Android secp256k1-kmp-jni-android, JVM secp256k1-kmp-jni-jvm, iOS Security framework
- Serves as a reference for good versus platform-specific abstraction calls in production KMP code
Adoption & trust: 819 installs on skills.sh; 1.5k GitHub stars; 2/3 security scanners passed (skills.sh audits).
Recommended Skills
Vercel React Native Skillsvercel-labs/agent-skills
Firebase Basicsfirebase/agent-skills
Building Native Uiexpo/skills
Firebase Ai Logic Basicsfirebase/agent-skills
Native Data Fetchingexpo/skills
Firebase Firestorefirebase/agent-skills
Journey fit
Primary fit
Build is where shared-module architecture choices are made before you multiply platform-specific actual implementations. Pm subphase fits decision documentation and rationale for abstraction boundaries, not day-to-day UI polish or deploy hooks.
Common Questions / FAQ
Is Kotlin Multiplatform safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Kotlin Multiplatform
# Abstraction Examples from Amethyst Codebase Real examples of abstraction decisions with rationale. ## Good Abstractions (Why They Work) ### 1. Secp256k1Instance - Crypto Signing **Location:** expect in commonMain, actual in androidMain/jvmMain/iosMain **Code:** ```kotlin // quartz/src/commonMain/.../Secp256k1Instance.kt expect object Secp256k1Instance { fun signSchnorr(data: ByteArray, privKey: ByteArray): ByteArray fun verifySchnorr(signature: ByteArray, hash: ByteArray, pubKey: ByteArray): Boolean } ``` **Why abstracted:** - Used by all platforms (Android, Desktop, iOS) - Security APIs fundamentally different: - Android: secp256k1-kmp-jni-android (Android Keystore integration) - Desktop: secp256k1-kmp-jni-jvm (pure JVM crypto) - iOS: Native Security framework - Core protocol requirement (Nostr signatures) **Decision rationale:** Always abstract crypto - varies by platform security APIs, critical for all platforms. --- ### 2. Log - Platform Logging **Location:** expect object in commonMain **Code:** ```kotlin // quartz/src/commonMain/.../Log.kt expect object Log { fun d(tag: String, message: String) fun w(tag: String, message: String, throwable: Throwable?) fun e(tag: String, message: String, throwable: Throwable?) } ``` **Why abstracted:** - Used throughout quartz module (protocol library) - Logging systems differ: - Android: android.util.Log - Desktop: println or logging framework - iOS: NSLog or OSLog - Simple interface, easy to implement **Decision rationale:** Often abstract logging - platform systems differ, widely used, simple interface. --- ### 3. Platform Utils - Time & Platform Name **Location:** expect functions in commonMain **Code:** ```kotlin // quartz/src/commonMain/.../Platform.kt expect fun platform(): String expect fun currentTimeSeconds(): Long ``` **Why abstracted:** - Used by Nostr event creation (timestamps) - Platform name for debugging - Simple utilities, clear platform boundary **Decision rationale:** Platform utilities are good abstraction candidates - simple, useful everywhere. --- ### 4. Jackson JSON (jvmAndroid Pattern) **Location:** jvmAndroid source set **Code:** ```kotlin // quartz/build.gradle.kts val jvmAndroid = create("jvmAndroid") { api(libs.jackson.module.kotlin) // JVM-only library } ``` **Why jvmAndroid (not commonMain):** - Jackson is JVM-specific library - Works on Android (JVM) + Desktop (JVM) - Does NOT work on iOS (not JVM) or web (not JVM) - Performance-critical JSON parsing **Decision rationale:** Use jvmAndroid for JVM libraries shared between Android and Desktop. **Future consideration:** For web support, migrate to kotlinx.serialization (works on all platforms). --- ## Bad/Over-Abstractions (Why They Failed) ### 1. Navigation Abstraction (Avoided) **What COULD have been done:** ```kotlin // ❌ Over-abstraction - DON'T DO THIS expect interface Navigator { fun navigate(route: String) fun popBackStack() } ``` **Why NOT abstracted:** - Navigation paradigms fundamentally different: - Android: Activity + Compose Navigation + back stack - Desktop: Window + screen state + no back stack concept - Complex APIs don't map well - Creates leaky abstraction **Actual approach:** Keep platform-specific - Android: `INav` interface + Compose Navigation - Desktop: Simple screen enum + state **Decision rationale:** Never abstract navigation - platforms too different, abstraction would be leaky. --- ### 2. String Resources (Abstraction Planned) **Current state:** Platform-specific (over-duplication) **Problem:** ```kotlin // Android uses R.string.* Text(stringResource(R.string.post_not_found)) // Desktop uses hardcoded strings Text("Post not found") ``` **Why NOT yet abstracted:** Waiting for second platform to fully implement UI, then will create StringProvider interface. **Planned abstraction:** ```kotlin // commonMain interface StringProvider { fun get(key: String): String } // androidMain class Androi