-
Notifications
You must be signed in to change notification settings - Fork 2
quick start
title: "Quick start" description: "Smallest end-to-end worker-kmp example — define a CoroutineWorker, wire DI, enqueue."
This walk-through gets a background sync worker running on all four platforms (Android, iOS, Desktop, Web) with the same commonMain code. We assume you already have a Kotlin Multiplatform project skeleton — if not, see Installation first.
Goal: by the end of this page you'll have a
DataSyncWorkerdefined incommonMain, wired through Koin, scheduled viaWorkManager.enqueue(...), and emitting state changes you can observe from any platform target.
The same worker class will execute on every platform you target. No expect / actual needed for the worker logic itself.
// commonMain/kotlin/com/example/work/DataSyncWorker.kt
package com.example.work
import io.github.mobilebytelabs.worker.CoroutineWorker
import io.github.mobilebytelabs.worker.WorkResult
import io.github.mobilebytelabs.worker.WorkerContext
class DataSyncWorker(
context: WorkerContext,
private val api: ApiClient,
) : CoroutineWorker(context) {
override suspend fun doWork(): WorkResult {
val endpoint = inputData.getString("endpoint")
?: return WorkResult.failure("missing endpoint")
return runCatching { api.sync(endpoint) }.fold(
onSuccess = { WorkResult.success(workDataOf("synced" to it.count)) },
onFailure = { WorkResult.retry(it.message) },
)
}
}Screenshot placeholder:
docs/_images/quick-start/step1-worker.png
Key points:
-
CoroutineWorkeris suspendable — call any suspending API fromdoWork(). - Return
WorkResult.success/failure/retry— semantics are identical on every platform. -
inputDatais a typed key-value bag — see WorkData docs in Home.
worker-kmp ships a single Koin DI module. Call it once at app start with your platform factory. Everything else — work scheduling, persistence, OS APIs — is handled internally by the library.
// commonMain/kotlin/com/example/di/AppModule.kt
import io.github.mobilebytelabs.worker.koin.workKoinModule
import io.github.mobilebytelabs.worker.koin.workerRegistry
import io.github.mobilebytelabs.worker.WorkerConfig
fun appKoinModule(platformFactory: WorkManagerFactory) = module {
single { ApiClient() }
}
fun startWorkerKoin(platformFactory: WorkManagerFactory) {
startKoin {
modules(
workKoinModule(
config = WorkerConfig(logLevel = LogLevel.INFO),
workers = workerRegistry {
register<DataSyncWorker> { ctx -> DataSyncWorker(ctx, get()) }
},
factory = platformFactory,
),
appKoinModule(platformFactory),
)
}
}Screenshot placeholder:
docs/_images/quick-start/step2-koin.png
Each platform's entry-point passes the platform factory:
// Android (Application.onCreate)
startWorkerKoin(androidWorkManagerFactory(this))
// iOS (commonMain main viewModel or AppDelegate Kotlin bridge)
startWorkerKoin(iosWorkManagerFactory())
// Desktop (main.kt)
startWorkerKoin(desktopWorkManagerFactory())
// Web (commonMain main + JS / Wasm entry)
startWorkerKoin(webWorkManagerFactory())The platform factory is the only per-platform code you write. Everything else — including foreground-service registration, BGTaskScheduler task IDs, daemon installation, and Service Worker registration — is handled by the library.
From any commonMain composable, ViewModel, or coroutine:
class SyncViewModel(private val workManager: WorkManager) {
fun startSync() {
workManager.enqueue(oneTimeWorkRequest<DataSyncWorker> {
setInputData(workDataOf("endpoint" to "/api/sync"))
setConstraints(Constraints {
setRequiredNetworkType(NetworkType.CONNECTED)
setRequiresBatteryNotLow(true)
})
setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30.seconds)
addTag("sync")
})
}
}Screenshot placeholder:
docs/_images/quick-start/step3-enqueue.png
The Constraints { … } DSL produces a builder whose semantics are identical on every platform. On Android they map to WorkManager.Constraints; on iOS to BGTaskScheduler predicates; on Desktop to in-process reachability + battery checks; on Web to navigator.connection + Service Worker sync events.
The Flow<List<WorkInfo>> API works the same on every platform:
class SyncScreen(workManager: WorkManager) {
val syncStatus: Flow<WorkInfoState> =
workManager.getWorkInfosByTag("sync")
.map { infos -> infos.firstOrNull()?.state ?: WorkInfoState.UNKNOWN }
}Or use the Compose Multiplatform UI components shipped in cmp-worker-compose:
@Composable
fun SyncDashboard() {
Column {
WorkSchedulerScreen(onWorkScheduled = { id -> /* navigate to detail */ })
WorkMonitorScreen(tag = "sync")
}
}Screenshot placeholder:
docs/_images/quick-start/step4-monitor.png
WorkMonitorScreen shows real-time state transitions, retry attempts, and progress events for every worker matching the tag. Drop it anywhere — it's the same composable on Android, iOS, Desktop, and Web.
- Platform-specific setup (Manifest / Info.plist / installer entries): Android · iOS · Desktop · Web
- Long-running tasks that must keep the OS awake: Foreground Tasks
- Telemetry — observe every worker lifecycle event from your OTel / Sentry / Firebase Perf SDK: Observers
- Web Push server setup for true-background Web workers: Web Push Server
- Migrating from v2.x: migrating-from-v2.md
Getting Started
Platform Support
Features
Operations
Release