### Optimize Parallel Execution - Good Example Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Group truly independent tasks to run in parallel. This example shows three independent sync tasks being started together. ```kotlin // These tasks don't depend on each other scheduler.beginWith(listOf( TaskRequest(workerClassName = "SyncContactsWorker"), TaskRequest(workerClassName = "SyncCalendarWorker"), TaskRequest(workerClassName = "SyncPhotosWorker") )) ``` -------------------------------- ### Background Task Scheduler - Begin With Task Chain Example Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Initiates the building of a task chain, starting with a single task or multiple parallel tasks, and enqueues the chain. ```kotlin // Sequential chain scheduler.beginWith(TaskRequest(workerClassName = "DownloadWorker")) .then(TaskRequest(workerClassName = "ProcessWorker")) .enqueue() // Parallel start scheduler.beginWith(listOf( TaskRequest(workerClassName = "SyncWorker"), TaskRequest(workerClassName = "CacheWorker") )) .then(TaskRequest(workerClassName = "FinalizeWorker")) .enqueue() ``` -------------------------------- ### Minimize Worker Setup Time (BAD vs GOOD) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/ios-best-practices.md Avoid heavy initialization within your worker's `doWork` method. Reuse shared instances for dependencies like databases and APIs to reduce setup time. ```kotlin // ❌ BAD: Heavy initialization class SlowWorker : IosWorker { override suspend fun doWork(input: String?, env: WorkerEnvironment): WorkerResult { val database = createDatabase() // 5s val api = initializeAPI() // 3s doActualWork() // 8s // Total: 16s wasted on setup return WorkerResult.Success() } } // ✅ GOOD: Reuse shared instances class FastWorker : IosWorker { override suspend fun doWork(input: String?, env: WorkerEnvironment): WorkerResult { val database = SharedDatabase.instance // <1ms val api = SharedAPI.instance // <1ms doActualWork() // 8s // Total: ~8s actual work return WorkerResult.Success() } } ``` -------------------------------- ### TaskChain Examples Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Illustrates various ways to construct and enqueue task chains, including sequential, parallel, and mixed execution. ```APIDOC ### Examples ```kotlin // Sequential execution scheduler .beginWith(TaskRequest(workerClassName = "DownloadWorker")) .then(TaskRequest(workerClassName = "ProcessWorker")) .then(TaskRequest(workerClassName = "UploadWorker")) .enqueue() // Parallel execution scheduler .beginWith(listOf( TaskRequest(workerClassName = "SyncWorker"), TaskRequest(workerClassName = "CacheWorker"), TaskRequest(workerClassName = "CleanupWorker") )) .then(TaskRequest(workerClassName = "FinalizeWorker")) .enqueue() // Mixed sequential and parallel scheduler .beginWith(TaskRequest(workerClassName = "DownloadWorker")) .then(listOf( TaskRequest(workerClassName = "ProcessImageWorker"), TaskRequest(workerClassName = "ProcessVideoWorker") )) .then(TaskRequest(workerClassName = "UploadWorker")) .enqueue() ``` ``` -------------------------------- ### Task Event Handling Examples Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Demonstrates how to emit `TaskCompletionEvent` from workers and collect these events in the UI. ```APIDOC ### Usage **Emitting events from workers:** ```kotlin class SyncWorker : IosWorker { override suspend fun doWork(input: String?, env: WorkerEnvironment): WorkerResult { return try { syncDataFromServer() TaskEventBus.emit( TaskCompletionEvent( taskName = "SyncWorker", success = true, message = "Data synced successfully", outputData = buildJsonObject { put("count", 100) } ) ) WorkerResult.Success(message = "Data synced successfully") } catch (e: Exception) { TaskEventBus.emit( TaskCompletionEvent( taskName = "SyncWorker", success = false, message = "Sync failed: ${e.message}" ) ) WorkerResult.Failure("Sync failed: ${e.message}") } } } ``` **Collecting events in UI:** ```kotlin @Composable fun TaskMonitor() { LaunchedEffect(Unit) { TaskEventBus.events.collect { event -> when { event.success -> { showSuccessToast(event.message) } else -> { showErrorToast(event.message) } } } } } ``` ``` -------------------------------- ### Example of OneTime Task Trigger Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md An example demonstrating how to configure `TaskTrigger.OneTime` to schedule a task for execution after a 5-second delay. ```kotlin TaskTrigger.OneTime(initialDelayMs = 5_000) // Execute after 5 seconds ``` -------------------------------- ### Example Mutation for IP Address Validation Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/release-notes/v2.3.2-RELEASE-NOTES.md Illustrates how mutation testing works by showing an original code snippet and potential mutations that should be caught by tests. This example targets IP address validation logic. ```kotlin // Original if (octets[0] == 10) return true // 10.0.0.0/8 // Mutation 1: Change constant if (octets[0] == 11) return true // Should FAIL SecurityValidatorTest // Mutation 2: Change operator if (octets[0] != 10) return true // Should FAIL testPrivateIPv4 ``` -------------------------------- ### BackgroundTaskScheduler - beginWith() Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Starts building a task chain, allowing for sequential or parallel task execution. ```APIDOC #### `beginWith()` Start building a task chain with a single task or multiple parallel tasks. ```kotlin fun beginWith(request: TaskRequest): TaskChain fun beginWith(requests: List): TaskChain ``` **Parameters:** - `request: TaskRequest` - Single task to start the chain - `requests: List` - Multiple tasks to run in parallel at the start **Returns:** `TaskChain` - Builder for constructing task chains **Example:** ```kotlin // Sequential chain scheduler.beginWith(TaskRequest(workerClassName = "DownloadWorker")) .then(TaskRequest(workerClassName = "ProcessWorker")) .enqueue() // Parallel start scheduler.beginWith(listOf( TaskRequest(workerClassName = "SyncWorker"), TaskRequest(workerClassName = "CacheWorker") )) .then(TaskRequest(workerClassName = "FinalizeWorker")) .enqueue() ``` ``` -------------------------------- ### Example Background Task Audit Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/ios-migration.md An example audit of background tasks, detailing their duration, frequency, constraints, criticality, and iOS compatibility. This helps in categorizing tasks for migration. ```kotlin // Example audit Task: DataSync - Duration: 45s - Frequency: Every 15 min - Constraints: Network, Battery Not Low - Critical: No - iOS Compatible: ❌ Too long Task: PushNotificationToken - Duration: 2s - Frequency: On app launch - Constraints: Network - Critical: Yes - iOS Compatible: ✅ Short and simple Task: FileUpload - Duration: 120s - Frequency: One-time - Constraints: WiFi, Charging - Critical: Yes - iOS Compatible: ❌ Too long, charging constraint ``` -------------------------------- ### Custom HttpClient configuration Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.3_TO_V2.3.4.md Example of how a custom `HttpClient` configuration is still respected after the v2.3.4 update, ensuring existing custom setups continue to work. ```kotlin val customClient = HttpClient { /* your config */ } val worker = HttpRequestWorker(httpClient = customClient) ``` -------------------------------- ### System Constraints Migration Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/constraints-triggers.md Example of migrating from old battery/storage/device idle triggers to new system constraints using the Constraints API. ```APIDOC ## System Constraints Migration ### Description Migrate from deprecated triggers like `BatteryLow`, `BatteryOkay`, `StorageLow`, and `DeviceIdle` to the new `systemConstraints` API within the `Constraints` class. ### Code Examples ```kotlin // Battery low condition Constraints(systemConstraints = setOf(SystemConstraint.ALLOW_LOW_BATTERY)) // Require battery not low Constraints(systemConstraints = setOf(SystemConstraint.REQUIRE_BATTERY_NOT_LOW)) // Storage low allowed Constraints(systemConstraints = setOf(SystemConstraint.ALLOW_LOW_STORAGE)) // Device idle required Constraints(systemConstraints = setOf(SystemConstraint.DEVICE_IDLE)) ``` ### Migration Table | Old trigger | New `SystemConstraint` | Platform | |----------------|-------------------------------|--------------| | `BatteryLow` | `ALLOW_LOW_BATTERY` | Android + iOS| | `BatteryOkay` | `REQUIRE_BATTERY_NOT_LOW` | Android + iOS| | `StorageLow` | `ALLOW_LOW_STORAGE` | Android only | | `DeviceIdle` | `DEVICE_IDLE` | Android only | ``` -------------------------------- ### WorkerDiagnostics Usage Example Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Demonstrates how to retrieve system health information and check for low power mode or low storage conditions. ```kotlin val diagnostics = WorkerDiagnostics.getInstance() val health = diagnostics.getSystemHealth() if (health.isLowPowerMode) { println("BGTasks may be throttled — device in low power mode") } if (health.isStorageLow) { println("Storage critical — tasks may fail") } ``` -------------------------------- ### Basic Sequential Task Chain Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Execute tasks one after another. Each task waits for the previous one to complete before starting. ```kotlin scheduler .beginWith(TaskRequest(workerClassName = "DownloadWorker")) .then(TaskRequest(workerClassName = "ProcessWorker")) .then(TaskRequest(workerClassName = "UploadWorker")) .enqueue() ``` -------------------------------- ### Manual Worker Factory Implementation (Before) Source: https://github.com/brewkits/kmpworkmanager/blob/main/kmpworker-ksp/README.md Example of a manual AndroidWorkerFactory implementation before using the KSP processor. ```kotlin class MyWorkerFactory : AndroidWorkerFactory { override fun createWorker(workerClassName: String): AndroidWorker? { return when (workerClassName) { "SyncWorker" -> SyncWorker() "UploadWorker" -> UploadWorker() else -> null } } } ``` -------------------------------- ### Initialize Koin with Factory (iOS) Source: https://github.com/brewkits/kmpworkmanager/blob/main/kmpworker/README.md Initialize Koin in your iOS application setup, including the KMPWorker module and a custom worker factory. ```kotlin // iOS - KoinSetup.kt fun initKoinIos() { startKoin { modules(kmpWorkerModule( workerFactory = MyWorkerFactory() // ✅ Required in v1.0.0 )) } } ``` -------------------------------- ### BackgroundTaskScheduler Interface Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Provides methods to start building a task chain. Can begin with a single task or a list of tasks. ```kotlin interface BackgroundTaskScheduler { fun beginWith(request: TaskRequest): TaskChain fun beginWith(requests: List): TaskChain } ``` -------------------------------- ### Basic Worker Implementation (Success/Failure) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Example of a worker that performs a sync operation and returns a `WorkerResult.Success` on completion or `WorkerResult.Failure` if an exception occurs. ```kotlin class SyncWorker : CommonWorker { override suspend fun doWork(input: String?): WorkerResult { return try { syncData() WorkerResult.Success(message = "Sync completed") } catch (e: Exception) { WorkerResult.Failure("Sync failed: ${e.message}") } } } ``` -------------------------------- ### Configure Platform-Specific Dependencies Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/TROUBLESHOOTING.md Ensure correct source set configuration for platform-specific dependencies. This example shows how to add the WorkManager KTX runtime dependency for Android and a placeholder for iOS. ```kotlin kotlin { sourceSets { androidMain.dependencies { implementation(libs.androidx.work.runtime.ktx) } iosMain.dependencies { // iOS platform dependencies } } } ``` -------------------------------- ### iOS AppDelegate Setup for Background Tasks Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/platform-setup.md Configure your AppDelegate to initialize Koin, register background tasks with BGTaskScheduler, and request notification permissions. This is crucial for enabling background execution of your KMP workers. ```swift import SwiftUI import BackgroundTasks import composeApp // Your shared framework name @main struct iOSApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate { // Your Swift bridge to the shared Koin graph. See `composeApp` demo's // `KoinIOS.kt` for a reference implementation — this is NOT a library API, // it's your own helper. private var koinIos: KoinIOS! func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Initialize Koin with your platform module // This module should include your WorkerFactory KoinInitializerKt.doInitKoin(platformModule: IOSModuleKt.iosModule) koinIos = KoinIOS() // Register background tasks registerBackgroundTasks() // Request notification permissions requestNotificationPermissions() return true } private func registerBackgroundTasks() { let scheduler = koinIos.getScheduler() let chainExecutor = koinIos.getChainExecutor() let dispatcher = koinIos.getDynamicTaskDispatcher() // 1. Master dispatcher — wakes up every dynamic task ID that is NOT // declared as its own BGTask identifier in Info.plist. Required. BGTaskScheduler.shared.register( forTaskWithIdentifier: "kmp_master_dispatcher_task", using: nil ) { task in IosBackgroundTaskHandler.shared.handleMasterDispatcherTask( task: task, dispatcher: dispatcher, scheduler: scheduler ) } // 2. Chain executor — batches task-chain execution. Required. BGTaskScheduler.shared.register( forTaskWithIdentifier: "kmp_chain_executor_task", using: nil ) { task in IosBackgroundTaskHandler.shared.handleChainExecutorTask( task: task, chainExecutor: chainExecutor ) } // 3. (Optional) Per-task identifiers. Only needed if you've also added // these specific IDs to `BGTaskSchedulerPermittedIdentifiers` to let // iOS schedule them directly instead of via the master dispatcher. // Skip this whole block unless you have a specific reason for it. // // let executor = koinIos.getSingleTaskExecutor() // BGTaskScheduler.shared.register( // forTaskWithIdentifier: "periodic-sync-task", // using: nil // ) { task in // IosBackgroundTaskHandler.shared.handleSingleTask( // task: task, // scheduler: scheduler, // executor: executor // ) // } } private func requestNotificationPermissions() { UNUserNotificationCenter.current().requestAuthorization( options: [.alert, .sound, .badge] ) { granted, error in if granted { print("Notification permission granted") } else if let error = error { print("Notification permission error: \(error)") } } } func applicationDidEnterBackground(_ application: UIApplication) { // Background tasks can now execute print("App entered background") } } ``` -------------------------------- ### Manual Mutation Example: SecurityValidator Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MUTATION-TESTING.md Demonstrates manual mutation of the `isPrivateIPv4` function in `SecurityValidator`. Introduces changes to operators, ranges, and conditional logic to test test robustness. ```kotlin fun isPrivateIPv4(hostname: String): Boolean { val parts = hostname.split(".") if (parts.size != 4) return false val octets = parts.map { it.toIntOrNull() ?: return false } return when { octets[0] == 10 -> true // 10.0.0.0/8 octets[0] == 172 && octets[1] in 16..31 -> true // 172.16.0.0/12 octets[0] == 192 && octets[1] == 168 -> true // 192.168.0.0/16 else -> false } } ``` ```kotlin octets[0] != 10 -> true // Should fail SecurityValidatorTest ``` ```kotlin octets[1] in 16..32 -> true // Should fail testPrivateIPv4 ``` ```kotlin // Comment out: octets[0] == 192 && octets[1] == 168 -> true // Should fail testBlockPrivate192_168Network ``` -------------------------------- ### Documenting iOS-Specific Behavior in Worker Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/ios-migration.md An example of documenting iOS-specific behavior within a worker's KDoc. This includes noting time limits, alternative syncing strategies, and how completion state is persisted. ```kotlin /** * Syncs data from server. * * **iOS Note**: Due to 30s time limit, only syncs essential data. * Media files are synced separately via [MediaSyncWorker]. * * **Force-Quit**: Uses [TaskEventManager] to persist completion state. */ class DataSyncWorker : IosWorker ``` -------------------------------- ### One-Shot Chain Scheduling (Coroutine Launch) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.3_TO_V2.3.4.md Example of scheduling a one-shot chain of tasks by wrapping the call in a coroutine launched on Dispatchers.IO. ```kotlin // Or wrap in launch: fun scheduleDataSync() { CoroutineScope(Dispatchers.IO).launch { scheduler.beginWith( TaskRequest("DownloadWorker") ).then( TaskRequest("ProcessWorker") ).then( TaskRequest("UploadWorker") ).enqueue() } } ``` -------------------------------- ### Setup KMP WorkManager on iOS Source: https://github.com/brewkits/kmpworkmanager/blob/main/README.md Configure AppDelegate for background task scheduling using BGTaskScheduler and Koin. Register your task identifier in Info.plist. ```swift @main class AppDelegate: UIResponder, UIApplicationDelegate { override init() { super.init() // IOSModuleKt.iosModule calls kmpWorkerModule(workerFactory = IosWorkerFactoryGenerated()) KoinInitializerKt.doInitKoin(platformModule: IOSModuleKt.iosModule) } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let koin = KoinIOS() BGTaskScheduler.shared.register( forTaskWithIdentifier: "kmp_chain_executor_task", using: nil ) { task in IosBackgroundTaskHandler.shared.handleChainExecutorTask( task: task, chainExecutor: koin.getChainExecutor() ) } return true } } ``` -------------------------------- ### Parallel Start, Sequential End Task Chain Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Initiates multiple tasks in parallel and then executes subsequent tasks sequentially. Ensure all tasks in the initial list are independent. ```kotlin scheduler .beginWith(listOf( TaskRequest(workerClassName = "FetchNewsWorker"), TaskRequest(workerClassName = "FetchWeatherWorker"), TaskRequest(workerClassName = "FetchStocksWorker") )) .then(TaskRequest(workerClassName = "MergeDataWorker")) .then(TaskRequest(workerClassName = "UpdateCacheWorker")) .then(TaskRequest(workerClassName = "NotifyUserWorker")) .enqueue() ``` -------------------------------- ### Implement Custom Download Worker Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/BUILTIN_WORKERS_GUIDE.md Example of a custom worker for production downloads, emphasizing the use of platform-specific managers for features like resume and parallel chunks. Requires advanced error handling. ```kotlin class ProductionDownloadWorker : Worker { override suspend fun doWork(input: String?): Boolean { // Use platform-specific download manager // Support resume, parallel chunks, background mode // Process response data // Advanced error handling } } ``` -------------------------------- ### Sequential, Then Parallel, Then Sequential Task Chain Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Starts with a single task, followed by a group of parallel tasks, and concludes with sequential tasks. Useful for workflows with distinct phases. ```kotlin scheduler .beginWith(TaskRequest(workerClassName = "PrepareDataWorker")) .then(listOf( TaskRequest(workerClassName = "ProcessImagesWorker"), TaskRequest(workerClassName = "ProcessVideosWorker"), TaskRequest(workerClassName = "ProcessAudioWorker") )) .then(TaskRequest(workerClassName = "CompressWorker")) .then(TaskRequest(workerClassName = "UploadWorker")) .enqueue() ``` -------------------------------- ### Schedule Weather Sync Every 15 Minutes Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/examples.md Use periodic tasks for recurring operations like syncing data. This example requires a network connection. ```kotlin scheduler.enqueue( id = "weather-sync", trigger = TaskTrigger.Periodic(intervalMs = 15.minutes.inWholeMilliseconds), workerClassName = "WeatherSyncWorker", constraints = Constraints(requiresNetwork = true) ) ``` -------------------------------- ### Mutation Testing Example Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MUTATION-TESTING.md Illustrates how introducing small code changes (mutations) should ideally cause tests to fail. If tests pass, it indicates potential weaknesses in the test suite. ```kotlin // Original code if (count > 0) { ... } // Mutation 1: Change operator if (count >= 0) { ... } // Should fail a test // Mutation 2: Change constant if (count > 1) { ... } // Should fail a test // Mutation 3: Negate condition if (count <= 0) { ... } // Should fail a test ``` -------------------------------- ### SyncWorker Implementation (iOS) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Example implementation of the IosWorker interface for a synchronization task. Ensure the doWork method completes within the required time frame. ```kotlin class SyncWorker : IosWorker { override suspend fun doWork(input: String?, env: WorkerEnvironment): WorkerResult { // Your implementation (must complete within 25 seconds for light tasks) return WorkerResult.Success(message = "Sync complete") } } ``` -------------------------------- ### ML Model Update Pipeline Task Chain Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md An example of an ML model update pipeline demonstrating task chaining with specific constraints for network, charging, and battery status. ```kotlin suspend fun updateMLModel() { scheduler .beginWith( TaskRequest( workerClassName = "DownloadMLModelWorker", constraints = Constraints( requiresNetwork = true, networkType = NetworkType.UNMETERED, requiresCharging = true ) ) ) .then( TaskRequest( workerClassName = "TrainMLModelWorker", constraints = Constraints( isHeavyTask = true, // Long-running task requiresCharging = true, requiresBatteryNotLow = true ) ) ) .then( TaskRequest( workerClassName = "ValidateModelWorker", constraints = Constraints( requiresCharging = true ) ) ) .then( TaskRequest( workerClassName = "UploadResultsWorker", constraints = Constraints( requiresNetwork = true ) ) ) .enqueue() } ``` -------------------------------- ### Complex Task Chain: Parallel Downloads → Sequential Processing → Parallel Uploads → Finalize Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/examples.md Combines sequential and parallel task execution for intricate workflows. This example demonstrates parallel downloads, sequential processing, and parallel uploads before a final cleanup task. ```kotlin scheduler // Step 1: Parallel downloads .beginWith(listOf( TaskRequest("DownloadImagesWorker"), TaskRequest("DownloadVideosWorker"), TaskRequest("DownloadDocsWorker") )) // Step 2: Sequential processing .then(TaskRequest("ValidateDownloadsWorker")) .then(TaskRequest("CompressMediaWorker")) // Step 3: Parallel uploads .then(listOf( TaskRequest("UploadToCloudWorker"), TaskRequest("UploadToBackupWorker") )) // Step 4: Finalize .then(TaskRequest("CleanupWorker")) .enqueue() ``` -------------------------------- ### Clone Repository and Build Project Source: https://github.com/brewkits/kmpworkmanager/blob/main/CONTRIBUTING.md Clone the KMP WorkManager repository and build the project using Gradle. Ensure you have JDK 17+ and Android Studio Hedgehog+ installed. ```bash git clone https://github.com/brewkits/kmpworkmanager.git cd kmpworkmanager ./gradlew build ``` -------------------------------- ### Schedule a One-Time Task Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/quickstart.md Schedule a task to run once with an initial delay. This example schedules a file upload task to run immediately, with exponential backoff retry on failure, and requires an unmetered network connection. ```kotlin suspend fun uploadFile() { scheduler.enqueue( id = "file-upload", trigger = TaskTrigger.OneTime( initialDelayMs = 0 // Execute immediately ), workerClassName = "UploadWorker", constraints = Constraints( requiresNetwork = true, networkType = NetworkType.UNMETERED, // WiFi only backoffPolicy = BackoffPolicy.EXPONENTIAL, backoffDelayMs = 10_000 // Retry after 10 seconds ) ) } ``` -------------------------------- ### Sequential Task Execution (Good Practice) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Illustrates a recommended approach for chaining tasks sequentially, where each task is focused on a single responsibility (e.g., Download, Validate, Process). This promotes modularity and maintainability. ```kotlin scheduler .beginWith(TaskRequest(workerClassName = "DownloadWorker")) .then(TaskRequest(workerClassName = "ValidateWorker")) .then(TaskRequest(workerClassName = "ProcessWorker")) .enqueue() ``` -------------------------------- ### Verify Build and Tests Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.3_TO_V2.3.4.md After migration, clean and rebuild the project, and run tests to ensure everything functions correctly. ```bash ./gradlew clean build ./gradlew test ``` -------------------------------- ### Optimize Parallel Execution - Bad Example Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Avoid parallelizing tasks that have dependencies. This example shows a bad practice where ProcessWorker depends on DownloadWorker, which would lead to failure if run in parallel. ```kotlin // ProcessWorker depends on DownloadWorker - don't parallelize! scheduler.beginWith(listOf( TaskRequest(workerClassName = "DownloadWorker"), TaskRequest(workerClassName = "ProcessWorker") // Will fail! )) ``` -------------------------------- ### Exact Alarm Scheduling Migration (v2.1.0 to v2.1.1) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/iOS-EXACT-ALARM-GUIDE.md This example shows the backward-compatible default behavior for exact alarm scheduling in KMP WorkManager. Version 2.1.1 explicitly documents the default SHOW_NOTIFICATION behavior for iOS, which was previously undocumented. ```kotlin // v2.1.0 and earlier scheduler.enqueue( id = "alarm", trigger = TaskTrigger.Exact(time), workerClassName = "AlarmWorker" ) // iOS: Showed notification (undocumented) // v2.1.1+ (same behavior, now explicit) scheduler.enqueue( id = "alarm", trigger = TaskTrigger.Exact(time), workerClassName = "AlarmWorker", constraints = Constraints( exactAlarmIOSBehavior = ExactAlarmIOSBehavior.SHOW_NOTIFICATION // Default ) ) // iOS: Shows notification (documented and explicit) ``` -------------------------------- ### HTTP Download with Checksum and Duplicate Policy Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/release-notes/v2.5.0-RELEASE-NOTES.md Configure an HTTP download with expected checksum verification and a policy for handling duplicate filenames. ```kotlin val cfg = HttpDownloadConfig( url = "https://cdn.example.com/file.bin", savePath = "$documents/file.bin", expectedChecksum = "e3b0c44…", checksumAlgorithm = ChecksumAlgorithm.SHA256, onDuplicate = DuplicatePolicy.RENAME, // → file.bin, file_1.bin, file_2.bin, … ) ``` -------------------------------- ### Initialize Koin and Register Background Tasks in AppDelegate Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/quickstart.md Set up Koin dependency injection and register the master dispatcher and chain executor background tasks in your iOSApp's initializer. Ensure your iosModule includes kmpWorkerModule. ```swift import SwiftUI import BackgroundTasks import composeApp @main struct iOSApp: App { init() { // Initialize Koin — your iosModule must include // kmpWorkerModule(workerFactory = IosWorkerFactoryGenerated()) KoinInitializerKt.doInitKoin(platformModule: IOSModuleKt.iosModule) // Register background tasks registerBackgroundTasks() } var body: some Scene { WindowGroup { ContentView() } } private func registerBackgroundTasks() { let koin = KoinIOS() let scheduler = koin.getScheduler() let dispatcher = koin.getDynamicTaskDispatcher() let chainExecutor = koin.getChainExecutor() // 1. Master dispatcher — handles every dynamic task ID // (everything not pre-registered as its own BGTask identifier). BGTaskScheduler.shared.register( forTaskWithIdentifier: "kmp_master_dispatcher_task", using: nil ) { IosBackgroundTaskHandler.shared.handleMasterDispatcherTask( task: $0, dispatcher: dispatcher, scheduler: scheduler ) } // 2. Chain executor — handles batched task chains. BGTaskScheduler.shared.register( forTaskWithIdentifier: "kmp_chain_executor_task", using: nil ) { IosBackgroundTaskHandler.shared.handleChainExecutorTask( task: $0, chainExecutor: chainExecutor ) } } } ``` -------------------------------- ### BackgroundTaskScheduler Interface Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Provides methods to start building a new task chain. ```APIDOC ## BackgroundTaskScheduler ```kotlin interface BackgroundTaskScheduler { fun beginWith(request: TaskRequest): TaskChain fun beginWith(requests: List): TaskChain } ``` ### Description The `BackgroundTaskScheduler` interface is the entry point for creating task chains. Use `beginWith` to start a new chain with a single `TaskRequest` or a list of `TaskRequest` objects. This method returns a `TaskChain` object, allowing you to further define the sequence of tasks. ``` -------------------------------- ### Get BackgroundTaskScheduler with Koin Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/quickstart.md Retrieve the BackgroundTaskScheduler instance directly using Koin. ```kotlin val scheduler: BackgroundTaskScheduler = get() ``` -------------------------------- ### Swift Usage for IosBackgroundTaskHandler Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Example of how to register and handle background tasks in Swift using the IosBackgroundTaskHandler. ```swift BGTaskScheduler.shared.register(forTaskWithIdentifier: "my-task", using: nil) { task in IosBackgroundTaskHandler.shared.handleSingleTask( task: task, scheduler: koin.getScheduler(), executor: koin.getExecutor() ) } ``` -------------------------------- ### Run Gradle Tests Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.2.2.md Execute existing tests using the Gradle wrapper to ensure backward compatibility after migration. ```bash ./gradlew test ``` -------------------------------- ### Android WorkManager Initialization in build.gradle.kts Source: https://github.com/brewkits/kmpworkmanager/blob/main/kmpworker/README.md Configures the Android build script to use `androidx.startup` for WorkManager initialization. ```kotlin // build.gradle.kts android { defaultConfig { manifestPlaceholders["workManagerInitProvider"] = "androidx.startup" } } ``` -------------------------------- ### Background Task Scheduler - Cancel Example Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Cancels a specific background task identified by its unique ID. ```kotlin scheduler.cancel("data-sync") ``` -------------------------------- ### Run Pitest and Open Report Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MUTATION-TESTING.md Commands to execute Pitest mutation testing via Gradle and open the generated HTML report in a web browser. ```bash ./gradlew pitest open kmpworker/build/reports/pitest/index.html ``` -------------------------------- ### Worker with Boolean Return (Before v2.3.0) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.0.md Example of a worker returning a Boolean value before upgrading to the WorkerResult API. ```kotlin class UploadWorker : CommonWorker { override suspend fun doWork(input: String?): Boolean { try { uploadFile(input) return true } catch (e: Exception) { println("Upload failed: ${e.message}") return false } } } ``` -------------------------------- ### Data Passing in Task Chains Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.0.md Demonstrates how to initiate a task chain and highlights the capability for workers to pass data to subsequent tasks, with FetchWorker returning data for ProcessWorker (future feature). ```kotlin scheduler.beginWith(TaskRequest("FetchWorker")) .then(TaskRequest("ProcessWorker")) .then(TaskRequest("SaveWorker")) .withId("data-pipeline", policy = ExistingPolicy.KEEP) .enqueue() // Workers can't pass data to each other! ❌ ``` ```kotlin // FetchWorker returns data class FetchWorker : CommonWorker { override suspend fun doWork(input: String?): WorkerResult { val data = fetchFromApi() return WorkerResult.Success( data = mapOf("apiData" to data) ) } } // ProcessWorker uses data (future feature - v2.4.0) // SaveWorker persists results ``` -------------------------------- ### Contradictory Constraints Example Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/constraints-triggers.md Avoid combining requiresDeviceIdle with expedited = true, as expedited tasks cannot wait for the device to be idle. ```kotlin // Don't do this! Constraints( requiresDeviceIdle = true, expedited = true // Expedited tasks can't wait for idle! ) ``` -------------------------------- ### Enqueueing Built-in Worker (Before v2.2.x) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.0.md Example of enqueuing a built-in worker before v2.3.0, highlighting the inability to retrieve results. ```kotlin scheduler.enqueue( id = "download-task", trigger = TaskTrigger.OneTime(), workerClassName = "dev.brewkits.kmpworkmanager.workers.builtins.HttpDownloadWorker", inputJson = Json.encodeToString(downloadConfig) ) // No way to get download results! ❌ ``` -------------------------------- ### Run Unit Tests Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/BUILTIN_WORKERS_GUIDE.md Execute unit tests for the kmpworker module using Gradle. ```bash ./gradlew :kmpworker:testDebugUnitTest ``` -------------------------------- ### Initialize KmpWorkManager with Log Configuration Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.2.2.md Initialize KmpWorkManager with custom logging levels. Use DEBUG for development and WARN for production to reduce log noise. ```kotlin class MyApp : Application() { override fun onCreate() { super.onCreate() KmpWorkManager.initialize( context = this, workerFactory = MyWorkerFactory(), config = KmpWorkManagerConfig( logLevel = if (BuildConfig.DEBUG) { Logger.Level.DEBUG_LEVEL } else { Logger.Level.WARN // Production: less noise } ) ) } } ``` -------------------------------- ### Kotlin: Provide Manual Sync Option Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/iOS-EXACT-ALARM-GUIDE.md If background sync might not run reliably, provide a manual option for the user to initiate the sync. This ensures data consistency and a better user experience. ```kotlin // If background sync might not run, give user manual option Button(onClick = { syncNow() }) { Text("Sync Now") } ``` -------------------------------- ### Conditional Chain Scheduling Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.3_TO_V2.3.4.md Example of building and enqueuing a chain of tasks conditionally based on a boolean flag. This function is a suspend function. ```kotlin suspend fun scheduleConditional(includeStep2: Boolean) { var chain = scheduler.beginWith(task1) if (includeStep2) { chain = chain.then(task2) } chain.enqueue() } ``` -------------------------------- ### One-Shot Chain Scheduling (Suspend) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.3_TO_V2.3.4.md Example of scheduling a one-shot chain of tasks using a suspend function for cleaner asynchronous code. ```kotlin // Make it suspend: suspend fun scheduleDataSync() { scheduler.beginWith( TaskRequest("DownloadWorker") ).then( TaskRequest("ProcessWorker") ).then( TaskRequest("UploadWorker") ).enqueue() } ``` -------------------------------- ### Configure iOS Quality of Service (QoS) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/constraints-triggers.md Set the `qos` hint for task execution priority on iOS. Options are HIGH, DEFAULT, and LOW, influencing how the system schedules the task. ```kotlin qos: QualityOfService = QualityOfService.DEFAULT ``` ```kotlin Constraints(qos = QualityOfService.HIGH) ``` -------------------------------- ### Initialize KmpWorkManager (Basic App) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.2.2.md Basic initialization of KmpWorkManager in your Application class. No changes are needed if you are on the latest version. ```kotlin class MyApp : Application() { override fun onCreate() { super.onCreate() KmpWorkManager.initialize(this, MyWorkerFactory()) } } ``` -------------------------------- ### Worker with WorkerResult API (After v2.3.0) Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.0.md Example of a worker using the new WorkerResult API to return structured data and success/failure messages. ```kotlin class UploadWorker : CommonWorker { override suspend fun doWork(input: String?): WorkerResult { return try { val result = uploadFile(input) WorkerResult.Success( message = "Uploaded ${result.size} bytes in ${result.duration}ms", data = mapOf( "fileSize" to result.size, "duration" to result.duration, "url" to result.url ) ) } catch (e: Exception) { WorkerResult.Failure("Upload failed: ${e.message}") } } } ``` -------------------------------- ### Host App Manifest: camera Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/ANDROID_FGS_GUIDE.md Declare `FOREGROUND_SERVICE_CAMERA` and `CAMERA` permissions, and configure the `foregroundServiceType` to include `camera` for tasks that require camera access in the background. ```xml ``` -------------------------------- ### Implement ProgressListener for Download/Upload Workers Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/BUILTIN_WORKERS_GUIDE.md Provide a `ProgressListener` to workers that handle downloads or uploads to track progress. This example shows integration with `HttpDownloadWorker`. ```kotlin class MyDownloadWorker( progressListener: ProgressListener? = null ) : HttpDownloadWorker(progressListener = progressListener) ``` -------------------------------- ### Handle Worker Failures in Android Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md On Android, workers should return Result.failure() or Result.retry() on failure. This example demonstrates returning Result.retry() for network exceptions. ```kotlin private suspend fun executeDownloadWorker(input: String?): Result { return try { downloadFile(input) Result.success() } catch (e: Exception) { Logger.e(LogTags.WORKER, "Download failed", e) Result.retry() // WorkManager will retry with backoff } } ``` -------------------------------- ### Run iOS Integration Tests Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/BUILTIN_WORKERS_GUIDE.md Execute integration tests on iOS platforms using Gradle. ```bash ./gradlew :kmpworker:iosTest ``` -------------------------------- ### Enqueue a Critical Task Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Example of enqueuing a task with CRITICAL priority using the scheduler. Ensure the worker class name is correctly specified. ```kotlin scheduler.beginWith( TaskRequest( workerClassName = "PaymentSyncWorker", priority = TaskPriority.CRITICAL ) ).enqueue() ``` -------------------------------- ### Run Android Integration Tests Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/BUILTIN_WORKERS_GUIDE.md Execute integration tests on Android devices or emulators using Gradle. ```bash ./gradlew :kmpworker:connectedAndroidTest ``` -------------------------------- ### Basic Parallel Task Execution Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Execute multiple independent tasks simultaneously. The subsequent task only starts after all parallel tasks have successfully completed. ```kotlin scheduler .beginWith(listOf( TaskRequest(workerClassName = "SyncContactsWorker"), TaskRequest(workerClassName = "SyncCalendarWorker"), TaskRequest(workerClassName = "SyncPhotosWorker") )) .then(TaskRequest(workerClassName = "FinalizeWorker")) .enqueue() ``` -------------------------------- ### Build project with Gradle Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MIGRATION_V2.3.3_TO_V2.3.4.md Command to execute a Gradle build after updating the dependency, which will reveal compilation errors related to the suspending `enqueue()` function. ```bash ./gradlew build ``` -------------------------------- ### Schedule Stock Price Updates Hourly Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/examples.md Schedule periodic tasks for frequent updates, such as stock prices. This example allows cellular network usage. ```kotlin scheduler.enqueue( id = "stock-updates", trigger = TaskTrigger.Periodic(intervalMs = 1.hours.inWholeMilliseconds), workerClassName = "StockPriceWorker", constraints = Constraints( requiresNetwork = true, requiresUnmeteredNetwork = false // Allow cellular ) ) ``` -------------------------------- ### Chain Multiple Tasks for Workflows Source: https://github.com/brewkits/kmpworkmanager/blob/main/README.md Use `scheduler.beginWith(...).then(...).enqueue()` to create multi-step workflows. This ensures that tasks execute sequentially and can resume from the point of interruption if the process is terminated. ```kotlin // Multi-step workflows that survive process death. // If step 47 of 100 was running when iOS killed the app — // the next BGTask invocation resumes at step 47, not step 0. scheduler.beginWith(TaskRequest("DownloadWorker", inputJson = "{\"url\":\"$fileUrl\"}")) .then(TaskRequest("ValidateWorker")) .then(TaskRequest("TranscodeWorker")) .then(TaskRequest("UploadWorker", inputJson = "{\"bucket\":\"processed\"}")) .withId("transcode-pipeline", policy = ExistingPolicy.KEEP) .enqueue() ``` -------------------------------- ### Handle Worker Failures in iOS Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Workers should return false on failure to stop the task chain. This example shows how to catch network exceptions and return false. ```kotlin class DownloadWorker : IosWorker { override suspend fun doWork(input: String?): Boolean { return try { downloadFile(input) true // Success } catch (e: NetworkException) { Logger.e(LogTags.WORKER, "Download failed", e) false // Failure - chain will stop } } } ``` -------------------------------- ### Run Property Tests with Gradle Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/release-notes/v2.3.2-RELEASE-NOTES.md Execute property-based unit tests for the kmpworker module using Gradle. This command targets tests with 'PropertyTest' in their name. ```bash ./gradlew :kmpworker:testDebugUnitTest --tests "*PropertyTest*" ``` -------------------------------- ### Batch Operations vs Individual Task Enqueuing Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/PERFORMANCE.md Batching operations into a single chain is more performant than enqueuing tasks individually. Use `beginWith` for efficient task management. ```kotlin // Good: Single chain with parallel tasks scheduler.beginWith(listOf(task1, task2, task3)) .enqueue() // Bad: Individual task calls scheduler.enqueue(task1) scheduler.enqueue(task2) scheduler.enqueue(task3) ``` -------------------------------- ### Add Assertions for Weak Tests Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/MUTATION-TESTING.md Example of strengthening a test by adding assertions for boundary conditions. This addresses cases where mutations might have been missed by the original test. ```kotlin @Test fun testPrivateIPv4DetectionComprehensive() { // Test boundary conditions assertTrue(SecurityValidator.validateURL("http://172.15.0.1")) // Just outside range assertFalse(SecurityValidator.validateURL("http://172.16.0.1")) // Start of range assertFalse(SecurityValidator.validateURL("http://172.31.255.255")) // End of range assertTrue(SecurityValidator.validateURL("http://172.32.0.1")) // Just outside range } ``` -------------------------------- ### Windowed Task Trigger Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/constraints-triggers.md Execute a task within a specified time window, between a start and end time. This is useful for off-peak processing and battery-friendly background tasks. ```APIDOC ## Windowed Task Trigger ### Description Execute a task within a time window (between start and end time). ### Parameters #### Path Parameters - `earliest` (Long) - Required - Window start time in epoch milliseconds - `latest` (Long) - Required - Window end time in epoch milliseconds. On iOS only `earliest` is used via `earliestBeginDate`; `latest` is logged but not enforced — the OS runs the task opportunistically. ### Request Example ```kotlin val now = Clock.System.now().toEpochMilliseconds() // Execute between 1 minute and 5 minutes from now TaskTrigger.Windowed( earliest = now + 60_000, latest = now + 5 * 60_000 ) // Execute between 2 PM and 4 PM today val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) val start = LocalDateTime(today.year, today.monthNumber, today.dayOfMonth, 14, 0) .toInstant(TimeZone.currentSystemDefault()) .toEpochMilliseconds() val end = LocalDateTime(today.year, today.monthNumber, today.dayOfMonth, 16, 0) .toInstant(TimeZone.currentSystemDefault()) .toEpochMilliseconds() TaskTrigger.Windowed(earliest = start, latest = end) ``` ### Platform Support - Android: ✅ - iOS: ⚠️ (best-effort — `latest` not enforced) ### Implementation Details - **Android**: `OneTimeWorkRequest` with delay and flex time window - **iOS**: Not supported ### Use Cases - Off-peak processing (e.g., "run between 2 AM - 4 AM") - Flexible scheduling - Battery-friendly background tasks ``` -------------------------------- ### Emit Task Progress Events in iOS Worker Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Emit TaskCompletionEvent from workers to track progress and status. This example shows emitting success or failure events from a DownloadWorker. ```kotlin class DownloadWorker : IosWorker { override suspend fun doWork(input: String?): Boolean { return try { downloadFile(input) TaskEventBus.emit( TaskCompletionEvent( taskName = "DownloadWorker", success = true, message = "✅ Download complete (Step 1/3)" ) ) true } catch (e: Exception) { TaskEventBus.emit( TaskCompletionEvent( taskName = "DownloadWorker", success = false, message = "❌ Download failed: ${e.message}" ) ) false } } } ``` -------------------------------- ### Sequential Task Chain: Download → Process → Upload Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/examples.md Use this for simple linear workflows where each task must complete before the next begins. Ensure worker class names are correctly specified. ```kotlin scheduler .beginWith(TaskRequest( workerClassName = "DownloadDataWorker", inputJson = "{\"url\": \"https://api.example.com/data\"}" )) .then(TaskRequest( workerClassName = "ProcessDataWorker" )) .then(TaskRequest( workerClassName = "UploadResultWorker" )) .enqueue() ``` -------------------------------- ### Provide Meaningful Task IDs Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/task-chains.md Assign descriptive IDs to tasks for easier debugging and monitoring. This example assigns IDs to download and train tasks in an ML pipeline. ```kotlin scheduler .beginWith( TaskRequest( id = "ml-pipeline-download", workerClassName = "DownloadMLModelWorker" ) ) .then( TaskRequest( id = "ml-pipeline-train", workerClassName = "TrainMLModelWorker" ) ) .enqueue() ``` -------------------------------- ### Emit Task Completion Event from Worker Source: https://github.com/brewkits/kmpworkmanager/blob/main/docs/api-reference.md Example of how to emit a `TaskCompletionEvent` from within a worker after completing its work. This includes emitting success or failure events with relevant details. ```kotlin class SyncWorker : IosWorker { override suspend fun doWork(input: String?, env: WorkerEnvironment): WorkerResult { return try { syncDataFromServer() TaskEventBus.emit( TaskCompletionEvent( taskName = "SyncWorker", success = true, message = "Data synced successfully", outputData = buildJsonObject { put("count", 100) } ) ) WorkerResult.Success(message = "Data synced successfully") } catch (e: Exception) { TaskEventBus.emit( TaskCompletionEvent( taskName = "SyncWorker", success = false, message = "Sync failed: ${e.message}" ) ) WorkerResult.Failure("Sync failed: ${e.message}") } } } ```