Skip to content

Releases: brewkits/KRelay

v2.1.1 — Compose Module, Hardened Concurrency & ProGuard-safe Persistence

06 Apr 01:20

Choose a tag to compare

What's new

krelay-compose — standalone Compose artifact

KRelayEffect<T> and rememberKRelayImpl<T> are now published as a separate artifact:

implementation("dev.brewkits:krelay-compose:2.1.1")

The module uses api(krelay) so all core types are visible to consumers automatically.

Hardened concurrency

Dispatch is now fully atomic: impl lookup, queue insertion, and persistence decision happen inside a single lock, closing the TOCTOU window that could permanently strand an action. Persistence I/O runs outside the lock so disk latency never blocks other threads.

ProGuard / R8 safe persistence

registerActionFactory and dispatchPersisted now require an explicit featureKey string. Class simple names can be obfuscated by R8; explicit keys survive minification unchanged. Old overloads are deprecated with replaceWith guidance.

Identity-aware unregister

unregister(impl) only removes the registration if the stored WeakRef points to the same object, preventing a recomposing Compose component from clearing a newer registration.

Thread-safe KRelayMetrics

All record* / get* / getAllMetrics() operations are now lock-protected.

iOS registration validation

registerFeature validates interface conformance at runtime — crashes immediately in debug mode, logs a clear warning in release. Prevents silent dispatch failures from KClass mismatches in Swift interop.

Priority eviction

Queue overflow now evicts the lowest-priority action, not the oldest FIFO item.


Installation

// shared/build.gradle.kts
commonMain.dependencies {
    implementation("dev.brewkits:krelay:2.1.1")
    implementation("dev.brewkits:krelay-compose:2.1.1") // optional
}

Migration from v2.1.0

  1. Update versions to 2.1.1.
  2. Compose users: if you were copying KRelayCompose.kt manually, delete it and add krelay-compose instead.
  3. Persistence users: replace implicit featureKey (based on class name) with an explicit stable string to avoid ProGuard issues.

See CHANGELOG for full details.

v2.1.0 - Compose Integration, Persistent Dispatch & Test Hardening

16 Mar 07:45
ad7d893

Choose a tag to compare

KRelay v2.1.0 Release Notes

Release Date: 2026-03-16
Type: Minor release — fully backward compatible with v2.0 and v1.x


Highlights

Compose Multiplatform Integration (Built-in)

Two new composable helpers ship in dev.brewkits.krelay.compose:

  • KRelayEffect<T> — registers a feature implementation scoped to the composition; auto-unregisters on dispose.
  • rememberKRelayImpl<T> — same as KRelayEffect but returns the implementation for further use.

Both accept an optional instance parameter for use with the Instance API.

// Zero-boilerplate registration
KRelayEffect<ToastFeature> {
    object : ToastFeature {
        override fun show(message: String) =
            Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
    }
}

A new KRelay.instance public property exposes the default KRelayInstance for cross-module access, fixing the internal visibility issue when KRelayCompose.kt lives in a different Gradle module.


Persistent Dispatch

New dispatchPersisted<T>() API survives process death via the ActionFactory pattern (serializable by design — no lambda capture):

  • KRelayPersistenceAdapter interface for pluggable storage backends
  • SharedPreferencesPersistenceAdapter for Android
  • NSUserDefaultsPersistenceAdapter for iOS
  • PersistedCommand with length-prefix serialization format (handles all special characters)

Scope Token API

Tag queued actions with a caller-identity token. Cancel all tagged actions without touching other pending actions for the same feature — ideal for ViewModel.onCleared():

class MyViewModel : ViewModel() {
    private val token = KRelay.scopedToken()

    fun doWork() {
        KRelay.dispatch<WorkFeature>(token) { it.run("task") }
    }

    override fun onCleared() {
        KRelay.cancelScope(token)  // removes only this ViewModel's queued actions
    }
}

Quality & Testing

  • 237 unit tests — all pass on JVM and iOS Simulator (Arm64)
  • 19 instrumented tests — all pass on real Android device (Pixel 6 Pro, Android 16)
  • Stress tests rewritten for KMP compatibility (iOS GCD async-safe — verify queue state synchronously)
  • resetConfiguration() on KRelayInstance and KRelay for isolated test setup

Bug Fixes

Fix Details
KRelayMetrics not wired recordDispatch/Queue/Replay() now fire correctly from all dispatch paths
metricsEnabled flag ignored if (!enabled) return guard added to each record* method
iOS KClass bridging broken KRelayIosHelperKt.getKClass(obj:) used during register(_:); all iOS operations now find correct interface key
Voyager demo lifecycle crash Upgraded to Voyager 1.1.0-beta03; replaced LaunchedEffect + detached scope with DisposableEffect + rememberCoroutineScope()
Android 15+ 16KB page alignment android.allow_non_16k_pages=true opt-in for apps using Peekaboo 0.5.2 (libimage_processing_util_jni.so is 4KB-aligned)
Duplicate registration debug log Warning emitted when register<T>() overwrites an existing live implementation
Test config pollution DiagnosticDemo tests now restore actionExpiryMs/maxQueueSize in @AfterTest

New Documentation

  • docs/COMPOSE_INTEGRATION.md — updated with KRelayEffect, rememberKRelayImpl, KRelay.instance
  • docs/LIFECYCLE.md — Android (Activity/Fragment/Compose) and iOS (UIViewController/SwiftUI) lifecycle best practices
  • docs/SWIFTUI_INTEGRATION.md — SwiftUI patterns, @Observable, NavigationStack, XCTest
  • samples/KRelayFlowAdapter.kt — Kotlin coroutines/Flow integration patterns

Installation

// commonMain
implementation("dev.brewkits:krelay:2.1.0")

Migration from v2.0.0

No changes required. All existing code works without modification.

Optional improvements:

  • Replace manual DisposableEffect registration blocks with KRelayEffect<T> or rememberKRelayImpl<T>
  • Use KRelay.instance instead of KRelay.defaultInstance if you were accessing it across modules
  • Add scopedToken() / cancelScope() in ViewModels for fine-grained queue cleanup

Compatibility

Kotlin KMP AGP Android minSdk iOS min
2.3.x 2.3.x 8.x 24 14.0

KRelay v2.0.0 - Instance API for Super Apps

04 Feb 04:15

Choose a tag to compare

KRelay v2.0.0 - Instance API for Super Apps 🚀

We're excited to announce KRelay v2.0.0, a major update that brings powerful new capabilities while maintaining 100% backward compatibility.

🎯 What's New

Instance-Based API

Create isolated KRelay instances for true module independence:

// Create isolated instances
val rideKRelay = KRelay.create("Rides")
val foodKRelay = KRelay.create("Food")

// Each module has independent registry - no conflicts!
rideKRelay.register<ToastFeature>(RideToastImpl())
foodKRelay.register<ToastFeature>(FoodToastImpl())

Perfect for Super Apps

If you're building a "Super App" (like Grab or Gojek) with multiple independent modules, v2.0 solves the feature name conflict problem:

Before (v1.x):

// Problem: Both modules use same feature name
KRelay.register<ToastFeature>(RideToastImpl())
KRelay.register<ToastFeature>(FoodToastImpl()) // ❌ Overwrites!

After (v2.0):

// Solution: Each module has its own instance
val rideKRelay = KRelay.create("Rides")
val foodKRelay = KRelay.create("Food")

rideKRelay.register<ToastFeature>(RideToastImpl())
foodKRelay.register<ToastFeature>(FoodToastImpl()) // ✅ No conflict!

DI-Friendly Architecture

Perfect integration with dependency injection frameworks:

// Koin module
val rideModule = module {
    single { KRelay.create("RideModule") }
    viewModel { RideViewModel(krelay = get()) }
}

// ViewModel
class RideViewModel(private val krelay: KRelayInstance) : ViewModel() {
    fun bookRide() {
        krelay.dispatch<ToastFeature> { it.show("Booking...") }
    }
}

Configurable Instances

Use the builder pattern for custom configuration:

val instance = KRelay.builder("MyModule")
    .maxQueueSize(50)
    .actionExpiry(60_000L)
    .debugMode(true)
    .build()

✨ Improvements

Developer Experience

  • Duplicate Scope Name Detection: Get warnings in debug mode when creating instances with duplicate names
  • Input Validation: Builder parameters validated with clear error messages
  • Better Error Messages: Know exactly what went wrong

Quality & Reliability

  • 10 New Isolation Tests: Comprehensive testing of multi-instance scenarios
  • 100% Test Pass Rate: All 15 instance tests passing
  • <5% Performance Overhead: Instance API is just as fast as singleton
  • ~800 bytes per Instance: Minimal memory footprint

📚 Documentation

🔄 Migration

Good news: Migration is optional!

  • No Breaking Changes: All v1.x code works without modification
  • Incremental Adoption: Migrate at your own pace
  • Simple Apps: Can continue using singleton API
  • New Projects: Start with instance API for better architecture

📦 Installation

// Gradle (build.gradle.kts)
dependencies {
    implementation("dev.brewkits:krelay:2.0.0")
}

🎓 Quick Start

Singleton API (Existing)

// Still works exactly as before
KRelay.dispatch<ToastFeature> { it.show("Hello!") }

Instance API (New)

// Create instance
val krelay = KRelay.create("MyScope")

// Use it
krelay.dispatch<ToastFeature> { it.show("Hello!") }

🏗️ Architecture

v2.0 uses a Facade Pattern:

  • Singleton API delegates to internal defaultInstance
  • Each instance has isolated registry, queue, and lock
  • Per-instance configuration
  • Thread-safe with fine-grained locking

🎯 Who Should Use v2.0 Instance API?

Highly Recommended for:

  • Super Apps with multiple independent modules
  • Projects using DI (Koin, Hilt)
  • Multi-team organizations
  • Large-scale apps with modular architecture

Optional for:

  • Simple single-module apps
  • Small to medium projects
  • Existing apps with singleton API (works fine)

⚡ Performance

  • Instance creation: ~0.4ms
  • Dispatch overhead: +2% vs singleton (negligible)
  • Memory per instance: ~800 bytes
  • 50 instances = ~40KB total

🙏 Acknowledgments

Thank you to the Kotlin Multiplatform community for feedback and support!

🔗 Links


Full Changelog: https://github.com/brewkits/KRelay/blob/main/CHANGELOG.md

v1.1.0 - Hardening Core

28 Jan 06:03

Choose a tag to compare

Thread Safety:

  • Replace pthread_mutex with NSRecursiveLock on iOS (ARC-managed, reentrant)
  • Add comprehensive stress tests (100k concurrent operations)
  • Validate thread safety on both platforms

Diagnostics & Monitoring:

  • Add dump() for visual debugging
  • Add getDebugInfo() for programmatic inspection
  • Add getRegisteredFeaturesCount() and getTotalPendingCount()
  • Add comprehensive diagnostic tests and demos

Developer Safety:

  • Add @MemoryLeakWarning annotation for lambda capture risks
  • Enhance documentation with memory management best practices
  • Update all guides to v1.1.0

Bug Fixes:

  • Fix main menu scroll issue
  • Fix toast overlap in update check demo
  • Replace fatalError with graceful degradation in Swift extensions

Test Coverage:

  • Android: 127/127 tests (100%)
  • iOS: 135/137 tests (98.5%)
  • Total: 262/264 tests (99.2%)

Breaking Changes: None - 100% backward compatible

v1.0.1 - iOS Platform Support

23 Jan 12:33

Choose a tag to compare

KRelay v1.0.1 - iOS Platform Support

This release adds separate Maven artifacts for iOS platforms to improve dependency resolution and build performance.

🆕 New Artifacts

  • krelay-iosarm64 - iOS devices (ARM64)
  • krelay-iossimulatorarm64 - iOS simulators on M1/M2 Macs (Apple Silicon)
  • krelay-iosx64 - iOS simulators on Intel Macs

📦 Installation

dependencies {
    implementation("dev.brewkits:krelay:1.0.1")
}

Gradle will automatically resolve the correct platform artifacts.

✨ Benefits

  • 🚀 Faster builds (platform-specific dependencies only)
  • 📦 Cleaner dependency resolution
  • 🎯 Better IDE support
  • 💾 Smaller download sizes per platform

📊 Bundle Details

  • File: krelay-1.0.1-bundle.zip (1.2MB)
  • Total Publications: 5 (base + android + 3 iOS platforms)
  • Total Files: 186 files (artifacts + checksums + GPG signatures)

🔄 Changelog

  • Add separate iOS platform publications for better dependency management
  • Maintain backward compatibility with v1.0.0
  • Generate complete Maven Central bundle with GPG signatures
  • Add comprehensive documentation for manual upload

📚 Documentation

🌐 Availability

Available on Maven Central:


Full Changelog: v1.0.0...v1.0.1

v1.0.0 - The Glue Code Standard

23 Jan 09:41

Choose a tag to compare

KRelay v1.0.0 - Production Ready 🎉

The Glue Code Standard for Kotlin Multiplatform

Safe, leak-free bridge between shared code and platform-specific APIs.


🚀 What's New

Core Features

Safe Dispatch

  • WeakReference-based registry (zero memory leaks)
  • Automatic cleanup on lifecycle events
  • Main thread execution guarantee

Sticky Queue

  • Preserves commands during configuration changes
  • Automatic replay when platform implementations register
  • Survives Activity/ViewController recreation

Thread Safety

  • Platform-specific locks (ReentrantLock on Android, pthread_mutex on iOS)
  • Fine-grained synchronization
  • Zero data races

Priority System

  • CRITICAL, HIGH, NORMAL, LOW priorities
  • Priority-based queue ordering
  • Emergency command support

Performance Monitoring

  • Metrics collection (dispatch count, queue size, replay count)
  • Performance profiling tools
  • Debug logging

Platform Support

  • Android (API 24+)
  • iOS (iOS 13+)

Integrations

Tested with popular KMP libraries:

  • ✅ Voyager (navigation)
  • ✅ Decompose (navigation)
  • ✅ Moko Permissions
  • ✅ Moko Biometry
  • ✅ Peekaboo (image picker)

📦 Installation

Add to your commonMain dependencies:

dependencies {
    implementation("dev.brewkits:krelay:1.0.0")
}

Maven Central: Publishing in progress. Manual upload required for first release.


🎯 Quick Start

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

// 2. Implement on platform
class AndroidToast(context: Context) : ToastFeature {
    override fun show(message: String) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
    }
}

// 3. Register in Activity/ViewController
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    KRelay.register<ToastFeature>(AndroidToast(this))
}

// 4. Dispatch from shared code
class LoginViewModel {
    fun onLoginSuccess() {
        KRelay.dispatch<ToastFeature> { it.show("Welcome!") }
    }
}

📚 Documentation


⚠️ Important Notes

Use Cases

✅ Perfect For:

  • Toast/Snackbar messages
  • Navigation commands
  • Permission requests
  • Haptic feedback
  • Analytics events (non-critical)

❌ Not For:

  • Returning values (use expect/actual)
  • State management (use StateFlow)
  • Critical operations (use WorkManager)
  • Background processing (use Dispatchers.IO)

Process Death

Important: KRelay queue does NOT survive process death (lambdas can't be serialized).

  • ✅ Safe for UI commands (Toast, Navigation)
  • ❌ Unsafe for critical operations (Payments, Data writes)

See ARCHITECTURE.md for detailed limitations.


🧪 Demo App

Run the demo app to see KRelay in action:

# Android
./gradlew :composeApp:installDebug

# iOS
open iosApp/iosApp.xcodeproj

Demo includes:

  • Basic features (Toast, Navigation)
  • Decompose integration (full navigation demo)
  • Real library integrations (Moko, Peekaboo)

🛠️ For Library Authors

KRelay is ideal for creating clean KMP library APIs:

Before (tightly coupled):

class MyViewModel(private val platformFeature: PlatformFeature) {
    // ViewModel depends on platform interface
}

After (clean separation):

class MyViewModel {
    fun doSomething() {
        KRelay.dispatch<PlatformFeature> { it.action() }
    }
}

🙏 Acknowledgments

Special thanks to:

  • Kotlin Multiplatform team
  • Compose Multiplatform team
  • Voyager, Decompose, Moko library authors
  • Early adopters and contributors

📝 Changelog

Added

  • Safe dispatch with WeakReference registry
  • Sticky queue with automatic replay
  • Thread-safe operations with platform locks
  • Priority-based action queue (CRITICAL, HIGH, NORMAL, LOW)
  • Performance metrics and monitoring
  • Queue management (max size, expiry)
  • Comprehensive test suite (90%+ coverage)
  • Integration demos (Voyager, Decompose, Moko)
  • Complete documentation

Fixed

  • Memory leaks from strong references
  • Lost commands during lifecycle changes
  • Thread safety issues
  • iOS Toast display on simulator

Documentation

  • Architecture guide
  • Integration guides for popular libraries
  • Testing guide with examples
  • Anti-patterns guide
  • Roadmap for future development

🚀 What's Next

v1.1.0 (Q2 2026): Desktop Support (Windows, macOS, Linux)

v1.2.0 (Q2 2026): Web/JS Support (Kotlin/JS, Wasm)

v2.0.0 (Q4 2026): Instance-based API for modularization

See ROADMAP.md for details.


📄 License

Apache License 2.0


Made with ❤️ by Nguyễn Tuấn Việt at Brewkits

Support: datacenter111@gmail.comCommunity: GitHub Issues