Files
gh-physics91-claude-vibe/skills/kotlin-multiplatform-reviewer/SKILL.md
2025-11-30 08:47:23 +08:00

8.0 KiB

name, description
name description
kotlin-multiplatform-reviewer WHEN: Kotlin Multiplatform (KMP) project review, expect/actual patterns, shared module structure, iOS interop WHAT: Module structure analysis + expect/actual validation + platform separation + iOS/Android interop + dependency management WHEN NOT: Android UI → kotlin-android-reviewer, Server → kotlin-spring-reviewer

Kotlin Multiplatform Reviewer Skill

Purpose

Reviews Kotlin Multiplatform (KMP) project structure and patterns including shared code design, expect/actual mechanism, and iOS interop.

When to Use

  • KMP project code review
  • "expect/actual", "shared module", "commonMain", "multiplatform" mentions
  • iOS/Android code sharing design review
  • Projects with kotlin("multiplatform") plugin

Project Detection

  • kotlin("multiplatform") plugin in build.gradle.kts
  • src/commonMain, src/androidMain, src/iosMain directories
  • shared or common module exists

Workflow

Step 1: Analyze Structure

**Kotlin**: 2.0.x
**Targets**: Android, iOS (arm64, simulatorArm64)
**Shared Module**: shared
**Source Sets**:
  - commonMain (shared code)
  - androidMain (Android specific)
  - iosMain (iOS specific)

Step 2: Select Review Areas

AskUserQuestion:

"Which areas to review?"
Options:
- Full KMP pattern check (recommended)
- Module structure/dependencies
- expect/actual implementation
- Platform code separation
- iOS interop (Swift/ObjC)
multiSelect: true

Detection Rules

Module Structure

Check Recommendation Severity
Bloated shared module Split by layer MEDIUM
Circular dependencies Unidirectional deps HIGH
Platform code in commonMain Move to androidMain/iosMain HIGH
Missing test module Add commonTest MEDIUM

Recommended Structure:

project/
├── shared/
│   └── src/
│       ├── commonMain/kotlin/    # Shared business logic
│       ├── commonTest/kotlin/    # Shared tests
│       ├── androidMain/kotlin/   # Android specific
│       ├── iosMain/kotlin/       # iOS specific
│       └── iosTest/kotlin/
├── androidApp/                    # Android app
└── iosApp/                       # iOS app (Xcode)

expect/actual Patterns

Check Recommendation Severity
actual without expect expect declaration required CRITICAL
Missing actual impl Provide actual for all targets CRITICAL
Excessive expect/actual Consider interface + DI MEDIUM
Direct platform API in actual Add abstraction layer MEDIUM
// commonMain - expect declaration
expect class Platform() {
    val name: String
    fun getDeviceId(): String
}

// androidMain - actual implementation
actual class Platform actual constructor() {
    actual val name: String = "Android ${Build.VERSION.SDK_INT}"
    actual fun getDeviceId(): String = Settings.Secure.getString(
        context.contentResolver,
        Settings.Secure.ANDROID_ID
    )
}

// iosMain - actual implementation
actual class Platform actual constructor() {
    actual val name: String = UIDevice.currentDevice.systemName()
    actual fun getDeviceId(): String = UIDevice.currentDevice
        .identifierForVendor?.UUIDString ?: ""
}

BAD: expect/actual overuse

// BAD: expect/actual for simple values
expect val platformName: String
actual val platformName: String = "Android"

// GOOD: interface + DI
interface PlatformInfo {
    val name: String
}

// androidMain
class AndroidPlatformInfo : PlatformInfo {
    override val name = "Android"
}

Platform Separation

Check Recommendation Severity
Platform import in commonMain Move to platform source set CRITICAL
Java class in commonMain expect/actual or pure Kotlin HIGH
UIKit/Android SDK in common Separate to platform source set CRITICAL
// BAD: Android import in commonMain
// commonMain/kotlin/Repository.kt
import android.content.Context  // Compile error!

// GOOD: expect/actual separation
// commonMain
expect class DataStore {
    fun save(key: String, value: String)
    fun get(key: String): String?
}

// androidMain
actual class DataStore(private val context: Context) {
    private val prefs = context.getSharedPreferences("app", Context.MODE_PRIVATE)
    actual fun save(key: String, value: String) {
        prefs.edit().putString(key, value).apply()
    }
    actual fun get(key: String): String? = prefs.getString(key, null)
}

// iosMain
actual class DataStore {
    actual fun save(key: String, value: String) {
        NSUserDefaults.standardUserDefaults.setObject(value, key)
    }
    actual fun get(key: String): String? =
        NSUserDefaults.standardUserDefaults.stringForKey(key)
}

iOS Interop

Check Recommendation Severity
Missing @ObjCName Swift-friendly naming LOW
Sealed class iOS exposure Use enum or @ObjCName MEDIUM
Direct Flow exposure to iOS Provide wrapper function HIGH
suspend function iOS call Provide completion handler wrapper HIGH
// BAD: Direct suspend function exposure
class Repository {
    suspend fun fetchData(): Data  // Hard to call from iOS
}

// GOOD: iOS wrapper provided
class Repository {
    suspend fun fetchData(): Data

    // iOS completion handler wrapper
    fun fetchDataAsync(completion: (Data?, Error?) -> Unit) {
        MainScope().launch {
            try {
                val data = fetchData()
                completion(data, null)
            } catch (e: Exception) {
                completion(null, e)
            }
        }
    }
}

Flow iOS Exposure:

// BAD: Direct Flow exposure
val dataFlow: Flow<Data>

// GOOD: iOS wrapper
fun observeData(onEach: (Data) -> Unit): Closeable {
    val job = MainScope().launch {
        dataFlow.collect { onEach(it) }
    }
    return object : Closeable {
        override fun close() { job.cancel() }
    }
}

Dependency Management

Check Recommendation Severity
Platform library in commonMain Use multiplatform library HIGH
Version mismatch Use Version Catalog MEDIUM
Unused dependencies Remove unused LOW

Multiplatform Library Recommendations:

Purpose Library
HTTP Ktor Client
Serialization Kotlinx Serialization
Async Kotlinx Coroutines
DI Koin, Kodein
Date/Time Kotlinx Datetime
Settings Multiplatform Settings
Logging Napier, Kermit
DB SQLDelight

Response Template

## KMP Project Review Results

**Project**: [name]
**Kotlin**: 2.0.x
**Targets**: Android, iOS (arm64, simulatorArm64)

### Module Structure
| Status | Item | Issue |
|--------|------|-------|
| OK | Source set separation | commonMain/androidMain/iosMain correct |
| MEDIUM | Tests | Add commonTest recommended |

### expect/actual
| Status | File | Issue |
|--------|------|-------|
| OK | Platform.kt | expect/actual correctly implemented |
| HIGH | DataStore.kt | Missing iosMain actual implementation |

### iOS Interop
| Status | Item | Issue |
|--------|------|-------|
| HIGH | Repository.kt | suspend function needs iOS wrapper |
| MEDIUM | UiState.kt | Add @ObjCName to sealed class |

### Recommended Actions
1. [ ] Add DataStore iosMain actual implementation
2. [ ] Add completion handler wrapper to fetchData()
3. [ ] Add commonTest source set

Best Practices

  1. Share Scope: Business logic > Data layer > UI (optional)
  2. expect/actual: Minimize usage, prefer interface + DI
  3. iOS Interop: Use SKIE library or manual wrappers
  4. Testing: Test shared logic in commonTest
  5. Dependencies: Prefer multiplatform libraries

Integration

  • kotlin-android-reviewer skill: Android specific code
  • kotlin-spring-reviewer skill: Server shared code
  • code-reviewer skill: General code quality

Notes

  • Based on Kotlin 2.0+
  • KMP 1.9.20+ recommended (Stable)
  • Compose Multiplatform requires separate review