-
Notifications
You must be signed in to change notification settings - Fork 2
single api guide
End-to-end consumer-facing guide for the worker-kmp single-API in commonMain. After this epic
ships (worker-kmp-single-api-completion), consumers write 100% commonMain code for the
worker-kmp scheduling/observation/init/Koin domain. Per-platform glue is auto-generated by
cmp-worker-app-plugin at build time.
// commonMain — entire worker-kmp setup
@WorkerKmpApp(
title = "My App",
iosBundleId = "com.example.myapp",
)
public fun appKoinModules(): List<Module> = listOf(
DataModule,
SyncObserverKoinModule, // from cmp-worker-sync — binds UniqueWorkObserver
)
@WorkerKmpWorkers(workers = [DataSyncWorker::class, NotificationWorker::class])
public fun workerDeclarations() = Unit
public class DataSyncWorker(
context: WorkerContext,
private val repo: CurrencyRepository, // public
) : CoroutineWorker(context) { /* ... */ }// androidMain (or your app's Application.onCreate)
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MyApp)
modules(appKoinModules())
}
WorkerKmpAuto.install() // single line — codegen handles the rest
}
}That's it. iosMain / desktopMain / wasmJsMain source sets have ZERO consumer-written files for the worker-kmp domain.
Use both @WorkerKmpApp + @WorkerKmpWorkers. The plugin generates per-platform launchers
(Android Application + Activity, iOS MainViewController, Desktop main(), Web main())
PLUS the worker-init files. Consumer's commonMain code is sufficient — no per-platform shell
classes needed.
Use only @WorkerKmpWorkers. The plugin skips launcher generation; consumer writes their own
Application.onCreate and calls WorkerKmpAuto.install() after startKoin { ... }. The
samples/kmp-project-template/cmp-android/AndroidApp.kt uses this shape (existing Coil setup,
language restoration, etc.).
- Goes on a top-level commonMain function (typically
public fun workerDeclarations() = Unit) -
workers: Array<KClass<*>>— each must extendCoroutineWorkerwithWorkerContextas first primary-constructor param + bepublic - OPTIONAL — omit if no workers needed (e.g. CMP app shell only)
- Within-module aggregation — multiple sites in the SAME module are aggregated; cross- module aggregation is NOT supported (place annotation in the module that depends on every worker-owning module)
- Co-annotation on worker classes — opts the worker into a subset of generated init files
- Default (annotation absent): worker registered on all 4 platforms
- Goes on
public fun appKoinModules(): List<Module>— REMOVED the v3.1.xfactory: WorkManagerFactoryparameter (codegen handles factory selection now)
All worker classes AND their primary-constructor dep types MUST be public. KSP processor
errors with clear suggested-fix message if you use internal class or private class —
codegen-emitted code lives in your cmp-shared/build/generated/... and can only reference
public symbols across module boundaries.
public class IntervalWorker(
ctx: WorkerContext,
val intervalMs: Long = 5000L, // SKIPPED from Koin autowiring — default used at runtime
) : CoroutineWorker(ctx) { ... }KSP detects KSValueParameter.hasDefault == true and emits
register<IntervalWorker> { ctx -> IntervalWorker(context = ctx) } (no getKoin().get<Long>()).
// ❌ Compile error — Koin runtime erases generic type args
public class GenericWorker(
ctx: WorkerContext,
val store: Store<String, ExchangeRates>,
) : CoroutineWorker(ctx)
// ✅ Use @Named qualifier
public class GenericWorker(
ctx: WorkerContext,
@Named("exchange-rates") val store: Store<String, ExchangeRates>,
) : CoroutineWorker(ctx)
// + matching Koin binding
single<Store<String, ExchangeRates>>(named("exchange-rates")) { ... }startKoin { androidContext(this) } MUST run BEFORE WorkerKmpAuto.install() — the Android
actual reads Context from the androidContext() binding. If called in wrong order, the
shim throws IllegalStateException with a clear message pointing at the fix.
Pure setup state — safe to call from Application.onCreate without runBlocking (eliminated
ANR risk vs v3.1.x's hypothetical suspend approach). No first-sync is enqueued — that's
consumer's responsibility after WorkerKmpAuto.install() returns (e.g.
get<WorkManager>().enqueueUniqueWork(...) from your own bootstrap code).
// Customize via Koin binding (D22 — annotations can't carry data-class instances)
val MyAppModule = module {
single { WorkerKmpHostConfig(logTag = "my-app.worker") }
}Defaults: koinScopeQualifier = null (global scope), logTag = "worker-kmp.host".
Koin module overrides — allowOverride = true is REQUIRED:
@Before
fun setUp() {
startKoin { modules(appKoinModules()) }
WorkerKmpAuto.install()
// Replace the codegen-bound WorkManager with a fake
loadKoinModules(
module {
single<WorkManager>(allowOverride = true) { FakeWorkManager() }
},
)
}For pure-commonMain unit tests of workers (no factory needed), instantiate the worker class
directly with a fake WorkerContext (same pattern existing worker-kmp samples already use).
| v3.1.x | v4.0.0 |
|---|---|
appKoinModules(factory: WorkManagerFactory): List<Module> |
appKoinModules(): List<Module> |
Manual workerRegistry { register<W> { … } } block |
@WorkerKmpWorkers(workers = [W::class]) |
loadKoinModules(workKoinModule(WorkerConfig(), registry, factory)) |
WorkerKmpAuto.install() |
Ad-hoc Sync.initialize(scheduler) host entry-point |
WorkerKmpHost.initialize() (called by codegen) |
Per-platform SyncManager actuals |
cmp-worker-sync's UniqueWorkObserver (commonMain-only) |
The old workKoinModule(config, workers, factory) function exists as a
@Deprecated(level = ERROR) tombstone with ReplaceWith("WorkerKmpAuto.install()") for IDE
quick-fix migration.
After migration, your consumer's per-platform source sets contain ZERO files for the worker-kmp scheduling/observation/init/Koin domain. Verify:
find your-app/src/{androidMain,iosMain,desktopMain,wasmJsMain} \
\( -name 'SyncManager*.kt' \
-o -name 'WorkScheduler*.kt' \
-o -name 'WorkerKmpAuto.kt' \
-o -name 'Sync*Initializer.kt' \) \
-not -path '*/build/*'Expected output: EMPTY.
- worker-kmp on GitHub
- Migration guide for v3.1.x consumers
- CHANGELOG — v4.0.0 breaking changes
Getting Started
Platform Support
Features
Operations
Release