Skip to content

feat(ui): add launchIO helper to BaseViewModel for background operations#128

Closed
therajanmaurya wants to merge 1 commit intodevfrom
feat/add-launchIO-helper-to-base-viewmodel
Closed

feat(ui): add launchIO helper to BaseViewModel for background operations#128
therajanmaurya wants to merge 1 commit intodevfrom
feat/add-launchIO-helper-to-base-viewmodel

Conversation

@therajanmaurya
Copy link
Member

@therajanmaurya therajanmaurya commented Feb 25, 2026

Summary

  • Add launchIO helper function to BaseViewModel that launches coroutines on Dispatchers.IO
  • Prevents UI freezing when performing network or database operations by ensuring they run on background threads

Problem

When using viewModelScope.launch { } without specifying a dispatcher, coroutines run on Dispatchers.Main by default. This causes the UI to freeze while waiting for API responses, even if the repository layer uses withContext(Dispatchers.IO), because the coroutine resumes on the Main thread.

Solution

Add a launchIO helper function that all ViewModels inheriting from BaseViewModel can use:

protected fun launchIO(block: suspend CoroutineScope.() -> Unit): Job {
    return viewModelScope.launch(Dispatchers.IO, block = block)
}

Usage

Replace:

viewModelScope.launch {
    val result = repository.makeApiCall()
    // UI frozen while waiting
}

With:

launchIO {
    val result = repository.makeApiCall()
    // Runs on IO thread, UI stays responsive
}

Why This Works

  • StateFlow.update {} is thread-safe and can be called from any thread
  • Compose's collectAsStateWithLifecycle() automatically observes on Main thread
  • Event channels handle cross-thread communication properly

Test plan

  • Build project successfully
  • Verify existing ViewModels continue to work
  • Update ViewModels to use launchIO for I/O operations

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Internal framework enhancements to improve asynchronous operation handling.

Note: This release contains no user-visible changes. Updates are limited to internal code improvements.

Add a launchIO helper function to BaseViewModel that launches coroutines
on Dispatchers.IO. This prevents UI freezing when performing network or
database operations by ensuring they run on background threads.

Usage:
- Replace `viewModelScope.launch { }` with `launchIO { }` for I/O operations
- StateFlow updates remain thread-safe
- Progress indicators animate smoothly during API calls

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

A new helper function launchIO was added to BaseViewModel, enabling coroutine launches on the IO dispatcher via viewModelScope. Associated imports for coroutine types were introduced to support the function signature and typing.

Changes

Cohort / File(s) Summary
IO Dispatcher Helper Function
core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt
Added launchIO() protected method that launches suspending blocks on Dispatchers.IO with Job return value, plus necessary coroutine type imports.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~4 minutes

Poem

🐰 A helper hops into the VM,
Dispatching IO with vim and verve,
No old paths disrupted, just new trails to serve,
Jobs returned like carrots, fresh and firm!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a launchIO helper function to BaseViewModel for handling I/O operations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-launchIO-helper-to-base-viewmodel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt (1)

105-115: KDoc overstates when Dispatchers.IO is required; clarify scope.

The comment implies launchIO should be used for all "network or database operations", but Dispatchers.IO is only needed if you have an API that blocks threads — if you use suspending functions, you can use any dispatcher, and you do not need Dispatchers.IO for network or database libraries that already provide suspending functions.

Using launchIO with modern suspend-based APIs (Ktor, Room with coroutines, SQLDelight) is harmless but misleading. The KDoc should clarify that this is specifically for blocking I/O calls. Otherwise, developers will cargo-cult launchIO for every use case, including ones where Dispatchers.Default or the default scope dispatcher is already correct.

📝 Suggested KDoc improvement
-    /**
-     * Launches a coroutine on [Dispatchers.IO] for network or database operations.
-     * Use this instead of `viewModelScope.launch` for any I/O-bound work to avoid
-     * blocking the main thread and causing UI freezes.
-     *
-     * `@param` block The suspending block to execute on the IO dispatcher.
-     * `@return` The [Job] representing the coroutine.
-     */
+    /**
+     * Launches a coroutine on [Dispatchers.IO] for **blocking** I/O operations
+     * (e.g., legacy blocking network/file APIs, JDBC, or any call that parks the thread).
+     *
+     * If you are using suspend-based libraries (Ktor, Room, SQLDelight, etc.), the library
+     * already dispatches work off the main thread internally; prefer plain `viewModelScope.launch`
+     * in that case.
+     *
+     * Note: unhandled exceptions in the block propagate to the scope's [CoroutineExceptionHandler].
+     *
+     * `@param` block The suspending block to execute on the IO dispatcher.
+     * `@return` The [Job] representing the coroutine.
+     */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt`
around lines 105 - 115, Update the KDoc for the protected function launchIO in
BaseViewModel to clarify that Dispatchers.IO is intended for blocking I/O
operations (e.g., legacy blocking file, socket, or JDBC calls) and not required
for modern suspending I/O libraries (e.g., Ktor, Room with coroutines,
SQLDelight) that are already non-blocking; mention that using launchIO with
suspending APIs is harmless but unnecessary and recommend using the default
viewModelScope dispatcher or Dispatchers.Default for CPU-bound work or when the
API is already suspend-friendly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt`:
- Around line 105-115: Update the KDoc for the protected function launchIO in
BaseViewModel to clarify that Dispatchers.IO is intended for blocking I/O
operations (e.g., legacy blocking file, socket, or JDBC calls) and not required
for modern suspending I/O libraries (e.g., Ktor, Room with coroutines,
SQLDelight) that are already non-blocking; mention that using launchIO with
suspending APIs is harmless but unnecessary and recommend using the default
viewModelScope dispatcher or Dispatchers.Default for CPU-bound work or when the
API is already suspend-friendly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4a85c9 and fce281f.

📒 Files selected for processing (1)
  • core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt

@therajanmaurya
Copy link
Member Author

Closing to create a new PR with the fix for Dispatchers.IO multiplatform compatibility issue.

TheKalpeshPawar added a commit to TheKalpeshPawar/kmp-project-template that referenced this pull request Mar 9, 2026
…rt (openMF#128)

- Add `KMPSQLDelightConventionPlugin` to manage SQLDelight configuration across platforms.
- Implement platform-specific `SqlDriver` setups for Android, iOS (Native), Desktop (SQLite), and Web (JS/Wasm).
- Introduce `MifosSQLDatabaseInitializer` to handle asynchronous schema creation.
- Add `SampleRepository` and `SQLDelightSampleRepositoryImpl` to demonstrate database operations.
- Update `DatabaseModule` and `DataModule` to include SQLDelight dependencies via Koin.
- Configure Web workers and `sql.js` for JS/Wasm support in `cmp-web`.
- Upgrade Gradle wrapper to version 9.1.0 and update project dependencies.
TheKalpeshPawar added a commit to TheKalpeshPawar/kmp-project-template that referenced this pull request Mar 9, 2026
…tform configuration (openMF#128)

* Add `SQLDelightRepositoryTest` common base class and platform-specific test implementations for Android, Desktop, Native, JS, and WasmJs.
* Update `testSQLDelightPlatformModule` across all platforms to use `single` instances for `SqlDriver` and ensure proper schema initialization.
* Refactor Koin module organization by moving `TestDatabaseModule` to `nonJsCommonTest` and removing the `DatabaseModule` include from `DataModule`.
* Update Android and Native test modules to use `Dispatchers.IO` directly for Room query contexts.
* Enhance JS/WasmJs test infrastructure with `karma.config.d` and `webpack.config.d` for `sql.js` WASM support.
* Enable browser testing for JS and WasmJs in `build.gradle.kts` with automatic browser detection.
* Update `libs.versions.toml` with `androidx-core` and `core-ktx` dependencies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant