DEV Community

Cover image for Mastering Native Interop in KMP: Why KRelay is the “Ultimate Bridge” Multiplatform
Viet, Nguyen Tuan
Viet, Nguyen Tuan

Posted on

Mastering Native Interop in KMP: Why KRelay is the “Ultimate Bridge” Multiplatform

KRelay: The Native Interop Bridge for Kotlin Multiplatform

KMP isn't just about sharing code; it's about managing Native UI safely. KRelay is the tool that makes it possible.


In my 15 years of mobile engineering — from J2ME to Flutter and now Kotlin Multiplatform (KMP) — one "final boss" always remains: Native Interop. How do you trigger native UI components like Toasts, Camera, or Navigation from shared code without causing memory leaks, threading crashes, or losing events during screen rotation?

Enter KRelay — a library designed with the Unix Philosophy: "Do one thing and do it well."


The Core Value: Why KRelay?

Traditionally, calling platform-specific code from shared ViewModels requires complex patterns:

  • Boilerplate Overload: You often need MutableSharedFlow + emit + collectAsState + LaunchedEffect just for a simple notification.
  • Memory Leaks: Strong references to an Activity or ViewController in shared code prevent garbage collection, leading to OutOfMemoryError.
  • Event Loss: If you dispatch a command while the UI is recreating (e.g., during rotation), that event is often lost forever.

KRelay solves these issues through three fundamental pillars:

1. Weak Registry

KRelay automatically manages WeakReferences to your platform implementations. When a ViewController or Activity is destroyed, the reference is cleared automatically.

2. Sticky Queue

If your ViewModel dispatches an action while the UI is not ready (e.g., during a cold start or rotation), KRelay queues the action and replays it as soon as the UI registers.

3. Safe Dispatch

All commands are automatically executed on the platform's Main/UI thread (using Looper on Android and GCD on iOS), preventing threading-related crashes.


The "Ultimate Combo": KRelay + Adapter Pattern

One of the biggest hurdles in 2026 is that many modern Swift libraries (like Lottie or Alamofire) are "Pure Swift" and are not visible to Kotlin's cinterop.

The Adapter Pattern combined with KRelay is the most powerful solution:

  • The Strategy: Define an interface in commonMain (e.g., NavFeature). Create a Swift Adapter that implements this interface and calls your 3rd-party Swift library.
  • The Benefit: Your ViewModel stays pure and testable. KRelay acts as the safe messenger, handling the threading and lifecycle complexities of the native implementation.

Technical Deep-Dive

KRelay is a high-performance Runtime bridge, avoiding the complexity and build-time overhead of KSP or reflection.

Installation

Add the library to your shared module's dependencies:

// shared/build.gradle.kts
commonMain.dependencies {
    implementation("dev.brewkits:krelay:1.0.0")
}
Enter fullscreen mode Exit fullscreen mode

Core API Usage

Defining and using a feature is straightforward:

// 1. Define (commonMain)
interface ToastFeature : RelayFeature {
    fun show(message: String)
}

// 2. Dispatch (Shared Logic)
KRelay.dispatch<ToastFeature> { it.show("Hello!") }

// 3. Register (Android/iOS Native)
KRelay.register<ToastFeature>(AndroidToastImpl(context))
Enter fullscreen mode Exit fullscreen mode

Advanced Features

  • Priority System: Use ActionPriority (LOW to CRITICAL) to ensure important events like error dialogs are replayed first.
  • Performance Metrics: Monitor dispatch counts and queue statistics per feature to optimize your app.
  • Safe Clean-up: Use clearQueue<T>() in your ViewModel's onCleared() to prevent lambda capture leaks.

Project Architecture

KRelay's internal structure is optimized for platform-specific efficiency:

KRelay/
├── commonMain/        # Core registry and queue logic
│   └── KRelay.kt, Priority.kt, Metrics.kt
├── androidMain/       # Android Looper and ReentrantLock
│   └── Lock.android.kt, MainThreadExecutor.android.kt
└── iosMain/           # iOS GCD and NSRecursiveLock
    └── Lock.ios.kt, MainThreadExecutor.ios.kt, KRelay+Extensions.swift
Enter fullscreen mode Exit fullscreen mode

Important Limitations

  • Process Death: KRelay's queue is in-memory and does not survive process death. Never use it for critical transactions like payments.
  • Singleton Scope: While simple for most apps, large Super Apps should use Feature Namespacing to avoid registry conflicts.

Verdict

KRelay isn't trying to be a state manager or a background worker. It is a focused, reliable messenger for one-way UI commands. By combining KRelay with the Adapter Pattern, you gain the flexibility to use any native library while maintaining the cleanest shared code architecture possible.

Start building safer KMP apps today.

GitHub Repository: github.com/brewkits/krelay


Tags: #KotlinMultiplatform #KMP #NativeInterop #AndroidDev #iOSDev #CleanArchitecture

Top comments (0)