Skip to content

cmp-share

cmp-share

Cross-platform share-sheet library for Kotlin Multiplatform.

Status: Experimental. All public APIs marked @ExperimentalShareApi. Ships alongside the other cmp-* modules at the shared kmptoolkit.version.

Features

  • Payload variants: text, URL, image bytes, file URI, multi (composite)
  • Platforms: Android, iOS, macOS, JVM Desktop, JS, wasmJs
  • Native UI: each platform uses its OS-native share surface — no custom Compose UI
  • Suspend API: integrates with structured concurrency; cancellation propagates correctly
  • Typed errors: sealed ShareError hierarchy + ShareResult for completion / cancel / failure
  • Zero third-party deps: native platform APIs only (kotlinx-coroutines + kotlinx-browser already in kmp-toolkit)

Platform support

Platform Backing API Notes
Android Intent.ACTION_SEND + Intent.createChooser Files via FileProvider (auto-wired); ACTION_SEND_MULTIPLE for Multi payloads
iOS UIActivityViewController Anchored to key-window root; iOS 14+
macOS NSSharingServicePicker Anchored to key-window contentView; macOS 11+
JVM (Desktop) System clipboard (text/url/image) + AWT FileDialog SAVE (file) Best-effort — NOT a native share sheet
JS / wasmJs navigator.share if available; else navigator.clipboard.writeText fallback Must invoke from a user-gesture handler (browser security)

Not targeted: tvOS, watchOS, Linux native, mingwX64, wasmWasi. Per cmp-toolkit Tier-3 exclusion policy. Adding them later requires upstream share-API coverage.

Install

// build.gradle.kts (your consumer app)
dependencies {
    val kmptoolkit = "3.2.13" // or latest — see https://central.sonatype.com/artifact/io.github.mobilebytelabs/cmp-share
    implementation("io.github.mobilebytelabs:cmp-share:$kmptoolkit")
}

Quick start

Share text

@OptIn(ExperimentalShareApi::class)
@Composable
fun ShareButton() {
    val scope = rememberCoroutineScope()
    Button(onClick = {
        scope.launch {
            val result = Share.text(
                content = "Hello from cmp-share!",
                options = ShareOptions(chooserTitle = "Share quote"),
            )
            when (result) {
                ShareResult.Completed -> showToast("Shared")
                ShareResult.Cancelled -> { /* user dismissed */ }
                is ShareResult.Failed -> showToast("Share failed: ${result.cause}")
            }
        }
    }) { Text("Share") }
}

Share a generated file

@OptIn(ExperimentalShareApi::class)
suspend fun shareReceipt(pdfUri: String) {
    Share.file(
        uri = pdfUri,
        mimeType = "application/pdf",
        filename = "receipt.pdf",
        options = ShareOptions(chooserTitle = "Send receipt"),
    )
}

Share an image

@OptIn(ExperimentalShareApi::class)
suspend fun shareSnapshot(pngBytes: ByteArray) {
    Share.image(bytes = pngBytes, mimeType = "image/png", filename = "snap.png")
}

Multi-payload (text + file + image)

@OptIn(ExperimentalShareApi::class)
suspend fun shareReport(summary: String, pdfUri: String, chartPng: ByteArray) {
    Share.multi(
        payloads = listOf(
            SharePayload.Text(summary),
            SharePayload.File(pdfUri, "application/pdf", "report.pdf"),
            SharePayload.Image(chartPng, "image/png", "chart.png"),
        ),
    )
}

Per-platform setup notes

Android — zero-config

The library declares its ShareInitProvider + FileProvider (authority ${applicationId}.cmp-share.fileprovider) via manifest-merger. Consumer apps don't need to declare anything.

iOS — present via key-window

Default: cmp-share traverses UIApplication.keyWindow.rootViewController.topMostController to find the presenting UIViewController. To anchor to a specific VC:

Share.text(
    content = "hi",
    options = ShareOptions(presentingController = myUIViewController),
)

JVM Desktop — best-effort, NOT a native share sheet

  • Text / URL → system clipboard write + log "copied to clipboard"
  • Image → clipboard image transferable
  • File → AWT FileDialog(SAVE) save-as prompt
  • Multi → first text-payload subset → clipboard, first file-payload → FileDialog

Documented loudly as "best-effort"; consumer apps that need a true native macOS share sheet on Desktop should run via macOS Compose-MP target.

JS / wasmJs — user-gesture required

// ✅ CORRECT — Share.share() inside Composable onClick (user-gesture activation)
Button(onClick = { scope.launch { Share.text("hi") } }) { ... }

// ❌ WRONG — LaunchedEffect is NOT a user-gesture call stack
// Browser blocks navigator.share() / navigator.clipboard.writeText()
LaunchedEffect(Unit) { Share.text("hi") }  // returns ShareResult.Failed(UserGestureMissing)

See also

Module reference

Module Identity (auto-gen)

Artifact Package Current version Maven Since API tier
io.github.mobilebytelabs:cmp-share com.mobilebytelabs.kmptoolkit.share UNKNOWN Central 2026-05-30 experimental

Module purpose (one paragraph):


§2 Per-Platform Parity Matrix (auto-gen)

Target Source-set present Real impl UnsupportedPlatform stub .kt count Last reviewed Coverage Notes
androidMain ✅ real 0 3 2026-06-01 full
iosMain ✅ real 0 1 2026-06-01 full
macosMain ✅ real 0 1 2026-06-01 full
jvmMain ✅ real 0 1 2026-06-01 full
jsMain ✅ real 0 1 2026-06-01 full
wasmJsMain ✅ real 0 1 2026-06-01 full
mingwMain 🟡 🟡 partial 3 1 2026-06-01 partial
linuxMain ✅ real 2 1 2026-06-01 full
tvosMain 🟡 🟡 wontfix-OS 2 1 2026-06-01 wontfix-OS
watchosMain 🟡 🟡 partial 4 1 2026-06-01 partial

Legend (Real impl): ✅ real impl, 🟡 partial / wontfix-OS / wontfix-infra / legacy stub, ⛔ not declared, — N/A. Legend (Coverage enum, since 2026-06-01): full (all public-API methods backed by OS primitive) · partial (most real; some typed UnsupportedPlatform fallbacks for contracts that don't apply) · wontfix-OS (OS lacks the primitive) · wontfix-infra (impl possible but CI/toolchain blocks it) · (legacy:full|stub) (auto-derived; pre-opt-in modules — add a // LD-2-coverage: {enum} comment to the platform's primary .kt file to graduate). See RULE-LIB-DEVELOPMENT-MD-001 LD-2 + ADRs for accepted wontfix cases.


API reference

Each release ships the module's full Dokka HTML site inside its -javadoc.jar artifact on Maven Central.

In IntelliJ / Android Studio the IDE mounts the jar and surfaces it automatically in hover popups, Quick Documentation, and Symbol search.