# How Platform-Specific Code Is Implemented Using Kotlin's expect/actual Pattern in the Muse Repository

> Learn how the Muse repository uses Kotlin's expect/actual pattern to implement platform-specific code, maximizing code reuse and isolating native SDK dependencies for Android and iOS.

- Repository: [Ko Shin/muse](https://github.com/kkoshin/muse)
- Tags: internals
- Published: 2026-03-05

---

**The Muse repository leverages Kotlin Multiplatform's expect/actual mechanism to declare abstract contracts in `commonMain` and provide concrete Android and iOS implementations in `androidMain` and `iosMain`, isolating native SDK dependencies while maximizing code reuse.**

The [Muse](https://github.com/kkoshin/muse) project is a Kotlin Multiplatform application targeting Android and iOS. To access platform-specific capabilities—such as file system paths, native logging, and UI components—without fragmenting the business logic, the codebase adopts the **Kotlin expect/actual pattern**. This architectural strategy defines lightweight API contracts in the shared module and fulfills them with platform-specific implementations in dedicated source sets.

## Declaring Platform Contracts in commonMain

The shared module (`commonMain`) contains pure Kotlin declarations that describe *what* the platform must provide, not *how* to provide it. These declarations use the `expect` keyword and act as compile-time boundaries between shared and platform code.

### Platform Detection and Constants

In [`src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.kt`](https://github.com/kkoshin/muse/blob/main/src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.kt), the repository defines an enum-based platform identifier:

```kotlin
expect val CURRENT_PLATFORM: Platform

enum class Platform {
    Android, Ios
}

```

This allows shared code to branch behavior based on the runtime platform while remaining testable on the JVM.

### File System Abstractions

Path management is abstracted through [`src/commonMain/kotlin/io/github/kkoshin/muse/repo/MusePathManager.kt`](https://github.com/kkoshin/muse/blob/main/src/commonMain/kotlin/io/github/kkoshin/muse/repo/MusePathManager.kt):

```kotlin
expect class MusePathManager {
    fun getCacheDir(): String
    fun getExportDir(): String
    fun getTempDir(): String
}

```

Additionally, [`src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.kt`](https://github.com/kkoshin/muse/blob/main/src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.kt) exposes `expect val SystemFileSystem: FileSystem` (from Okio) for low-level file operations.

### Database Driver Factory

For local data persistence, [`src/commonMain/kotlin/io/github/kkoshin/muse/repo/SqlDriver.kt`](https://github.com/kkoshin/muse/blob/main/src/commonMain/kotlin/io/github/kkoshin/muse/repo/SqlDriver.kt) declares:

```kotlin
expect class DriverFactory {
    fun createDriver(): SqlDriver
}

```

This enables SQLDelight usage across platforms without exposing platform-specific driver initialization to the shared domain layer.

### Platform Information and Logging

UI and diagnostic utilities follow the same pattern. [`src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/PlatformInfo.kt`](https://github.com/kkoshin/muse/blob/main/src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/PlatformInfo.kt) defines:

```kotlin
expect fun rememberPlatformSpecificInfo(): PlatformSpecificInfo

interface PlatformSpecificInfo {
    val versionName: String
    val versionCode: Int
    val exportFolderPath: String
    fun openUrl(url: String)
}

```

Similarly, [`src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/logcat.kt`](https://github.com/kkoshin/muse/blob/main/src/commonMain/kotlin/io/github/kkoshin/muse/platformbridge/logcat.kt) provides a unified logging interface:

```kotlin
expect inline fun logcat(tag: String = "Muse", block: () -> String)

```

## Android Implementations in androidMain

The `androidMain` source set supplies `actual` implementations that bridge to the Android SDK.

### Platform and Path Resolution

In [`src/androidMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.android.kt`](https://github.com/kkoshin/muse/blob/main/src/androidMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.android.kt):

```kotlin
actual val CURRENT_PLATFORM: Platform = Platform.Android

```

The file manager implementation in [`src/androidMain/kotlin/io/github/kkoshin/muse/repo/MusePathManager.android.kt`](https://github.com/kkoshin/muse/blob/main/src/androidMain/kotlin/io/github/kkoshin/muse/repo/MusePathManager.android.kt) resolves paths via `Context`:

```kotlin
actual class MusePathManager(private val context: Context) {
    actual fun getCacheDir(): String = context.cacheDir.absolutePath
    actual fun getExportDir(): String = context.getExternalFilesDir(null)?.absolutePath 
        ?: context.filesDir.absolutePath
    actual fun getTempDir(): String = context.cacheDir.absolutePath
}

```

### Database and System Services

The SQLDelight driver is instantiated in [`src/androidMain/kotlin/io/github/kkoshin/muse/repo/SqlDriver.android.kt`](https://github.com/kkoshin/muse/blob/main/src/androidMain/kotlin/io/github/kkoshin/muse/repo/SqlDriver.android.kt):

```kotlin
actual class DriverFactory(private val context: Context) {
    actual fun createDriver(): SqlDriver {
        return AndroidSqlDriver(Database.Schema, context, "muse.db")
    }
}

```

Platform information and logging are handled in [`src/androidMain/kotlin/io/github/kkoshin/muse/platformbridge/PlatformInfo.android.kt`](https://github.com/kkoshin/muse/blob/main/src/androidMain/kotlin/io/github/kkoshin/muse/platformbridge/PlatformInfo.android.kt) and [`src/androidMain/kotlin/io/github/kkoshin/muse/platformbridge/logcat.android.kt`](https://github.com/kkoshin/muse/blob/main/src/androidMain/kotlin/io/github/kkoshin/muse/platformbridge/logcat.android.kt), respectively. The former reads `BuildConfig.VERSION_NAME` and launches URLs via `Intent.ACTION_VIEW`, while the latter delegates to `android.util.Log.d`.

## iOS Implementations in iosMain

The `iosMain` source set provides counterparts using Kotlin/Native interop with Apple frameworks.

### Platform Identification and File Management

[`src/iosMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.ios.kt`](https://github.com/kkoshin/muse/blob/main/src/iosMain/kotlin/io/github/kkoshin/muse/platformbridge/Platform.ios.kt) defines:

```kotlin
actual val CURRENT_PLATFORM: Platform = Platform.Ios

```

Path resolution in [`src/iosMain/kotlin/io/github/kkoshin/muse/repo/MusePathManager.ios.kt`](https://github.com/kkoshin/muse/blob/main/src/iosMain/kotlin/io/github/kkoshin/muse/repo/MusePathManager.ios.kt) utilizes `NSFileManager`:

```kotlin
actual class MusePathManager {
    actual fun getCacheDir(): String = NSTemporaryDirectory()
    actual fun getExportDir(): String = NSFileManager.defaultManager.URLForDirectory(
        NSDocumentDirectory, NSUserDomainMask, null, true, null
    )?.path ?: ""
    actual fun getTempDir(): String = NSTemporaryDirectory()
}

```

### Native UI and Diagnostics

[`src/iosMain/kotlin/io/github/kkoshin/muse/platformbridge/PlatformInfo.ios.kt`](https://github.com/kkoshin/muse/blob/main/src/iosMain/kotlin/io/github/kkoshin/muse/platformbridge/PlatformInfo.ios.kt) implements version retrieval via `NSBundle.mainBundle` and URL opening with `SFSafariViewController` or `UIApplication.sharedApplication.openURL`. Logging in [`src/iosMain/kotlin/io/github/kkoshin/muse/platformbridge/logcat.ios.kt`](https://github.com/kkoshin/muse/blob/main/src/iosMain/kotlin/io/github/kkoshin/muse/platformbridge/logcat.ios.kt) simplifies to:

```kotlin
actual inline fun logcat(tag: String, block: () -> String) {
    println("[$tag] ${block()}")
}

```

The SQL driver factory in [`src/iosMain/kotlin/io/github/kkoshin/muse/repo/DriverFactory.ios.kt`](https://github.com/kkoshin/muse/blob/main/src/iosMain/kotlin/io/github/kkoshin/muse/repo/DriverFactory.ios.kt) returns a native `SqlDriver` configured for iOS SQLite.

## Consuming Platform APIs in Shared Code

Shared domain classes consume these abstractions without import statements for platform classes. For example, a version reporting service in `commonMain` remains pure Kotlin:

```kotlin
class VersionReporter {
    private val info = rememberPlatformSpecificInfo()
    
    fun reportVersion() {
        logcat { "App version: ${info.versionName} (${info.versionCode})" }
    }
}

```

During compilation, the Kotlin compiler substitutes the appropriate `actual` implementation based on the target platform. The shared code invokes `rememberPlatformSpecificInfo()` and `logcat()` without knowing whether it will execute on Android or iOS.

## Summary

- **Abstract Contracts**: The `commonMain` module defines `expect` declarations for platform detection, file paths, database drivers, logging, and UI helpers in files like [`Platform.kt`](https://github.com/kkoshin/muse/blob/main/Platform.kt), [`MusePathManager.kt`](https://github.com/kkoshin/muse/blob/main/MusePathManager.kt), and [`PlatformInfo.kt`](https://github.com/kkoshin/muse/blob/main/PlatformInfo.kt).
- **Android Fulfillment**: The `androidMain` source set provides `actual` implementations in [`Platform.android.kt`](https://github.com/kkoshin/muse/blob/main/Platform.android.kt), [`MusePathManager.android.kt`](https://github.com/kkoshin/muse/blob/main/MusePathManager.android.kt), and [`SqlDriver.android.kt`](https://github.com/kkoshin/muse/blob/main/SqlDriver.android.kt), bridging to Android SDK classes such as `Context` and `SQLiteOpenHelper`.
- **iOS Fulfillment**: The `iosMain` source set supplies `actual` implementations in [`Platform.ios.kt`](https://github.com/kkoshin/muse/blob/main/Platform.ios.kt), [`MusePathManager.ios.kt`](https://github.com/kkoshin/muse/blob/main/MusePathManager.ios.kt), and [`DriverFactory.ios.kt`](https://github.com/kkoshin/muse/blob/main/DriverFactory.ios.kt), utilizing Kotlin/Native interop with `NSFileManager`, `NSBundle`, and native SQLite.
- **Zero Platform Leakage**: Shared business logic references only the `expect` APIs, ensuring compile-time safety and enabling unit testing on the JVM without mobile emulators.

## Frequently Asked Questions

### What is the purpose of the expect/actual pattern in Kotlin Multiplatform?

The expect/actual pattern allows developers to define a common API surface in shared Kotlin code using the `expect` keyword, then provide platform-specific implementations using the `actual` keyword in platform-specific source sets. This mechanism enables the shared module to remain pure Kotlin while delegating to native SDKs like Android's `Context` or iOS's `Foundation` framework.

### How does Muse handle file system differences between Android and iOS?

Muse abstracts file system access through the `MusePathManager` class. In `commonMain`, it is declared as an `expect class` with methods like `getCacheDir()` and `getExportDir()`. The `androidMain` implementation uses `Context.getExternalFilesDir()` and `Context.cacheDir`, while the `iosMain` implementation uses `NSFileManager` and `NSTemporaryDirectory()`, allowing shared code to manipulate paths uniformly.

### Can the Muse codebase support additional platforms like Desktop or Web?

Yes. Because the platform-specific contracts are isolated in discrete `expect` declarations, adding support for JVM Desktop or JavaScript would require creating new source sets (e.g., `desktopMain` or `jsMain`) and providing `actual` implementations for each `expect` function and class. The existing `commonMain` logic would remain unchanged.

### Where are the platform-specific implementations located in the repository?

Android implementations reside under `src/androidMain/kotlin/io/github/kkoshin/muse/`, typically in files suffixed with [`.android.kt`](https://github.com/kkoshin/muse/blob/main/.android.kt) (e.g., [`Platform.android.kt`](https://github.com/kkoshin/muse/blob/main/Platform.android.kt), [`MusePathManager.android.kt`](https://github.com/kkoshin/muse/blob/main/MusePathManager.android.kt)). iOS implementations are located under `src/iosMain/kotlin/io/github/kkoshin/muse/` with the [`.ios.kt`](https://github.com/kkoshin/muse/blob/main/.ios.kt) suffix (e.g., [`Platform.ios.kt`](https://github.com/kkoshin/muse/blob/main/Platform.ios.kt), [`PlatformInfo.ios.kt`](https://github.com/kkoshin/muse/blob/main/PlatformInfo.ios.kt)).