# Built-in Workers Guide ## Overview KMP WorkManager provides 5 production-ready built-in workers that cover common background task scenarios. These workers are implemented in pure Kotlin Multiplatform code and work identically on Android and iOS. **Purpose**: Demo/reference implementations showing how to build native workers. For production apps with complex requirements, consider implementing custom workers using platform-specific libraries. --- ## Quick Start ### 1. Initialize with Built-in Workers ```kotlin // Initialize KMP WorkManager with built-in workers only KmpWorkManager.initialize( context = this, workerFactory = BuiltinWorkerRegistry ) ``` ### 2. Or Combine with Custom Workers ```kotlin class MyWorkerFactory : WorkerFactory { override fun createWorker(workerClassName: String): Worker? { return when(workerClassName) { "MyCustomWorker" -> MyCustomWorker() else -> null } } } // Compose custom + built-in workers KmpWorkManager.initialize( context = this, workerFactory = CompositeWorkerFactory( MyWorkerFactory(), // Try custom workers first BuiltinWorkerRegistry // Fall back to built-in ) ) ``` --- ## iOS Setup For iOS apps using built-in workers, you need to configure background task identifiers: ### 1. Update Info.plist Add built-in worker task IDs to `BGTaskSchedulerPermittedIdentifiers`: ```xml BGTaskSchedulerPermittedIdentifiers demo-builtin-httprequest demo-builtin-httpsync demo-builtin-httpdownload demo-builtin-httpupload demo-builtin-filecompression ``` ### 2. Update Worker Factory (iOS Sample App) If you're using a custom `IosWorkerFactory`, integrate `BuiltinWorkerRegistry`: ```kotlin import dev.brewkits.kmpworkmanager.workers.BuiltinWorkerRegistry class IosWorkerFactory { fun createWorker(workerClassName: String): IosWorker? { return when (workerClassName) { // Your custom workers "MyCustomWorker" -> MyCustomWorker() else -> { // Try builtin workers val builtinWorker = BuiltinWorkerRegistry.createWorker(workerClassName) if (builtinWorker != null) { WorkerAdapter(builtinWorker) } else { null } } } } } // Adapter to wrap library Worker as IosWorker private class WorkerAdapter(private val worker: Worker) : IosWorker { override suspend fun doWork(input: String?): Boolean { return worker.doWork(input) } } ``` ### 3. Update NativeTaskScheduler (if using custom implementation) Add built-in worker task IDs to the permitted list: ```kotlin val PERMITTED_TASK_IDS = setOf( // Your existing task IDs // Built-in worker task IDs "demo-builtin-httprequest", "demo-builtin-httpsync", "demo-builtin-httpdownload", "demo-builtin-httpupload", "demo-builtin-filecompression" ) ``` --- ## Available Workers ### 1. HttpRequestWorker **Purpose**: Execute HTTP requests without processing responses (fire-and-forget) **Use Cases**: - Analytics events - Webhook notifications - Health check pings - Simple API calls **Configuration**: ```kotlin val config = HttpRequestConfig( url = "https://api.example.com/analytics", method = "POST", headers = mapOf( "Authorization" to "Bearer token", "Content-Type" to "application/json" ), body = """{"event":"app_opened","timestamp":${System.currentTimeMillis()}}""", timeoutMs = 30000 ) val inputJson = Json.encodeToString(HttpRequestConfig.serializer(), config) scheduler.enqueue( id = "analytics-event", trigger = TaskTrigger.OneTime(), workerClassName = "HttpRequestWorker", inputJson = inputJson, constraints = Constraints(requiresNetwork = true) ) ``` **Performance**: - Memory: ~2-3MB RAM - Startup: <50ms **Supported Methods**: GET, POST, PUT, DELETE, PATCH --- ### 2. HttpSyncWorker **Purpose**: JSON synchronization (POST/GET JSON data with response) **Use Cases**: - Data synchronization - Batch analytics uploads - Periodic data sync - API sync endpoints **Configuration**: ```kotlin val requestBody = buildJsonObject { put("lastSyncTime", System.currentTimeMillis() - 3600000) put("deviceId", "device-123") } val config = HttpSyncConfig( url = "https://api.example.com/sync", method = "POST", headers = mapOf("Authorization" to "Bearer token"), requestBody = requestBody, timeoutMs = 60000 ) val inputJson = Json.encodeToString(HttpSyncConfig.serializer(), config) scheduler.enqueue( id = "data-sync", trigger = TaskTrigger.Periodic(intervalMs = 3600000), // Every hour workerClassName = "HttpSyncWorker", inputJson = inputJson, constraints = Constraints(requiresNetwork = true) ) ``` **Performance**: - Memory: ~3-5MB RAM - Startup: <50ms - Default timeout: 60 seconds **Features**: - Automatic JSON content-type - Response body logging (truncated) --- ### 3. HttpDownloadWorker **Purpose**: Download files from HTTP/HTTPS URLs **Use Cases**: - File downloads - App updates - Media downloads - Document downloads **Configuration**: ```kotlin val config = HttpDownloadConfig( url = "https://example.com/large-file.zip", savePath = "/storage/downloads/file.zip", headers = mapOf("User-Agent" to "MyApp/1.0"), timeoutMs = 300000 // 5 minutes ) val inputJson = Json.encodeToString(HttpDownloadConfig.serializer(), config) scheduler.enqueue( id = "download-file", trigger = TaskTrigger.OneTime(), workerClassName = "HttpDownloadWorker", inputJson = inputJson, constraints = Constraints( requiresNetwork = true, requiresCharging = true // Only download when charging ) ) ``` **Performance**: - Memory: ~3-5MB RAM (constant, regardless of file size) - Supports files: GB+ (streaming download) - Default timeout: 5 minutes **Features**: - Streaming downloads (no memory spikes) - Atomic file operations (writes to .tmp then renames) - Auto-creates parent directories - Progress tracking support --- ### 4. HttpUploadWorker **Purpose**: Upload files using multipart/form-data **Use Cases**: - Photo/video uploads - Document uploads - File sharing - Profile picture updates **Configuration**: ```kotlin val config = HttpUploadConfig( url = "https://api.example.com/upload", filePath = "/storage/photo.jpg", fileFieldName = "photo", fileName = "profile.jpg", mimeType = "image/jpeg", headers = mapOf("Authorization" to "Bearer token"), fields = mapOf( "userId" to "123", "albumId" to "profile-photos" ), timeoutMs = 120000 // 2 minutes ) val inputJson = Json.encodeToString(HttpUploadConfig.serializer(), config) scheduler.enqueue( id = "upload-photo", trigger = TaskTrigger.OneTime(), workerClassName = "HttpUploadWorker", inputJson = inputJson, constraints = Constraints(requiresNetwork = true) ) ``` **Performance**: - Memory: ~5-7MB RAM - Default timeout: 2 minutes **Features**: - Multipart/form-data encoding - Auto MIME type detection (40+ file types) - Additional form fields support - Custom filename support **Supported MIME Types**: Images (jpg, png, gif, webp, heic), Videos (mp4, mov, avi, webm), Audio (mp3, wav, ogg), Documents (pdf, doc, xls, ppt), Archives (zip, tar, gz, 7z) --- ### 5. FileCompressionWorker **Purpose**: Compress files/directories into ZIP archives **Use Cases**: - Log archiving - Backup preparation - Storage optimization - Batch file compression **Configuration**: ```kotlin val config = FileCompressionConfig( inputPath = "/storage/logs", outputPath = "/storage/backups/logs.zip", compressionLevel = "high", // low, medium, high excludePatterns = listOf("*.tmp", ".DS_Store", "*.lock"), deleteOriginal = false ) val inputJson = Json.encodeToString(FileCompressionConfig.serializer(), config) scheduler.enqueue( id = "compress-logs", trigger = TaskTrigger.OneTime(), workerClassName = "FileCompressionWorker", inputJson = inputJson, constraints = Constraints( requiresCharging = true, requiresDeviceIdle = true ) ) ``` **Performance**: - Memory: Platform-dependent - Compression levels: low (fast), medium (balanced), high (best) **Features**: - Recursive directory compression - Exclude patterns (*.tmp, .DS_Store, etc.) - Optional source deletion - Compression statistics logging **Platform Implementations**: - **Android**: java.util.zip.ZipOutputStream - **iOS**: Foundation APIs (basic), recommend ZIPFoundation for production --- ## Extension Functions For easier usage, import extension functions: ```kotlin import dev.brewkits.kmpworkmanager.workers.builtins.scheduleHttpRequest import dev.brewkits.kmpworkmanager.workers.builtins.scheduleDownload import dev.brewkits.kmpworkmanager.workers.builtins.scheduleUpload import dev.brewkits.kmpworkmanager.workers.builtins.scheduleCompression // Simplified scheduling scheduler.scheduleHttpRequest( id = "ping-api", url = "https://api.example.com/ping", method = "POST", body = """{"status":"active"}""" ) scheduler.scheduleDownload( id = "download-file", url = "https://example.com/file.zip", savePath = "/downloads/file.zip", requiresCharging = true ) scheduler.scheduleUpload( id = "upload-photo", url = "https://api.example.com/upload", filePath = "/storage/photo.jpg", headers = mapOf("Authorization" to "Bearer token") ) scheduler.scheduleCompression( id = "compress-logs", inputPath = "/storage/logs", outputPath = "/storage/logs.zip", compressionLevel = "high" ) ``` --- ## Security Features All workers include built-in security validation: ### URL Validation - Only http:// and https:// schemes allowed - Prevents file:// and other dangerous schemes ### File Path Validation - Prevents path traversal attacks (../) - Validates absolute paths ### Size Limits - Request body: 10MB max - Response body: 50MB max (logged) ### Safe Logging - URLs sanitized (no query params logged) - Request/response bodies truncated - Sensitive data redacted --- ## Configuration Reference ### HttpRequestConfig ```kotlin data class HttpRequestConfig( val url: String, // Required: http:// or https:// val method: String = "GET", // GET, POST, PUT, DELETE, PATCH val headers: Map? = null, // Optional headers val body: String? = null, // Optional body for POST/PUT/PATCH val timeoutMs: Long = 30000 // Default: 30 seconds ) ``` ### HttpSyncConfig ```kotlin data class HttpSyncConfig( val url: String, // Required: http:// or https:// val method: String = "POST", // GET, POST, PUT, PATCH val headers: Map? = null, // Optional headers val requestBody: JsonElement? = null, // Optional JSON body val timeoutMs: Long = 60000 // Default: 60 seconds ) ``` ### HttpDownloadConfig ```kotlin data class HttpDownloadConfig( val url: String, // Required: http:// or https:// val savePath: String, // Required: absolute path val headers: Map? = null, // Optional headers val timeoutMs: Long = 300000 // Default: 5 minutes ) ``` ### HttpUploadConfig ```kotlin data class HttpUploadConfig( val url: String, // Required: http:// or https:// val filePath: String, // Required: file to upload val fileFieldName: String, // Required: form field name val fileName: String? = null, // Optional: custom filename val mimeType: String? = null, // Optional: auto-detected if null val headers: Map? = null, // Optional headers val fields: Map? = null, // Optional form fields val timeoutMs: Long = 120000 // Default: 2 minutes ) ``` ### FileCompressionConfig ```kotlin data class FileCompressionConfig( val inputPath: String, // Required: file or directory val outputPath: String, // Required: output .zip path val compressionLevel: String = "medium", // low, medium, high val excludePatterns: List? = null, // Optional: *.tmp, .DS_Store val deleteOriginal: Boolean = false // Delete source after compress ) ``` --- ## Testing ### Run Unit Tests ```bash ./gradlew :kmpworker:testDebugUnitTest ``` ### Test Coverage - Configuration validation: 100% - Serialization/deserialization: 100% - Worker factory: 100% - Security validation: 100% ### Integration Tests For full integration tests (HTTP, file I/O), use platform-specific test suites: ```bash # Android integration tests ./gradlew :kmpworker:connectedAndroidTest # iOS integration tests ./gradlew :kmpworker:iosTest ``` --- ## Best Practices ### 1. Network Constraints Always set `requiresNetwork = true` for HTTP workers: ```kotlin constraints = Constraints(requiresNetwork = true) ``` ### 2. Battery Optimization For heavy operations, require charging: ```kotlin constraints = Constraints( requiresCharging = true, requiresDeviceIdle = true // iOS only ) ``` ### 3. Error Handling Workers return `true` on success, `false` on failure. WorkManager will retry failed workers based on backoff policy. ### 4. Timeouts Set appropriate timeouts based on operation: - Quick API calls: 30 seconds - Sync operations: 60 seconds - Downloads: 5+ minutes - Uploads: 2+ minutes ### 5. Progress Tracking For download/upload workers, provide ProgressListener: ```kotlin class MyDownloadWorker( progressListener: ProgressListener? = null ) : HttpDownloadWorker(progressListener = progressListener) ``` --- ## Limitations These are **demo/reference implementations** with some limitations: ### General Limitations - Fire-and-forget pattern (no response processing in most workers) - No retry logic (handled by WorkManager) - No request/response interceptors - No advanced error handling ### HttpRequestWorker / HttpSyncWorker - Cannot process response data (can only check status code) - No cookie management - No response caching ### HttpDownloadWorker - No resume support - No parallel chunk downloads - No background download delegation (iOS URLSession background) ### HttpUploadWorker - Loads entire file into memory (not streaming) - No chunked upload - No resume support ### FileCompressionWorker - iOS implementation is basic (recommends ZIPFoundation for production) - No encryption support - No password protection - No multi-volume archives --- ## Production Recommendations For production apps with complex requirements: ### 1. Use Platform-Specific Libraries **Flutter/Dart Layer**: - HTTP: Use `dio`, `http` packages (better than Ktor) - Download: Use `flutter_downloader` - Upload: Use `flutter_uploader` - Compression: Use `archive` package **React Native Layer**: - HTTP: Use `axios`, `fetch` API - Download: Use `react-native-fs`, `rn-fetch-blob` - Upload: Use `react-native-upload` - Compression: Use `react-native-zip-archive` ### 2. Implement Custom Workers For advanced features, implement custom workers: ```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 } } ``` ### 3. When to Use Built-in Workers Use built-in workers for: - ✅ Simple fire-and-forget analytics - ✅ Basic health checks - ✅ Simple file operations - ✅ Demo/prototype apps - ✅ Learning/reference implementations Don't use built-in workers for: - ❌ Complex API integrations requiring response processing - ❌ Large file operations requiring resume/pause - ❌ Production apps with strict requirements - ❌ Advanced features (caching, interceptors, auth flows) --- ## Dependencies Built-in workers use these libraries: ```kotlin // Ktor Client 3.0.3 - HTTP operations implementation("io.ktor:ktor-client-core:3.0.3") implementation("io.ktor:ktor-client-okhttp:3.0.3") // Android implementation("io.ktor:ktor-client-darwin:3.0.3") // iOS // Okio 3.9.1 - File I/O implementation("com.squareup.okio:okio:3.9.1") // Kotlinx Serialization - JSON handling implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") ``` --- ## Troubleshooting ### Worker not found ``` Error: Worker class not found: HttpRequestWorker ``` **Solution**: Make sure you initialized with `BuiltinWorkerRegistry` or `CompositeWorkerFactory`. ### Network error ``` Error: HTTP request failed: Connection refused ``` **Solution**: Check `requiresNetwork = true` in constraints. Verify URL is reachable. ### File not found ``` Error: File does not exist: /storage/file.jpg ``` **Solution**: Verify file path is correct and file exists. Check file permissions. ### Timeout error ``` Error: Request timed out ``` **Solution**: Increase `timeoutMs` in config. Check network speed. ### Compression failed (iOS) ``` Warning: iOS ZIP compression requires ZIPFoundation library ``` **Solution**: Current iOS implementation is basic. For production, integrate ZIPFoundation library. --- ## FAQ **Q: Can I process HTTP response data?** A: HttpRequestWorker only checks status codes. For response processing, implement a custom worker or use HttpSyncWorker which logs responses. **Q: Do workers support authentication flows?** A: Workers support custom headers (Bearer tokens, API keys). For OAuth flows, implement custom worker. **Q: Can I resume failed downloads?** A: No. Built-in workers don't support resume. Use platform-specific download managers for this feature. **Q: Are workers production-ready?** A: They're demo/reference implementations. For production with complex requirements, use platform-specific libraries or custom workers. **Q: Can I use workers without network?** A: Only FileCompressionWorker works offline. HTTP workers require network connectivity. **Q: How do I handle worker failures?** A: WorkManager automatically retries failed workers based on backoff policy. Workers return `false` on failure. **Q: Can I chain workers?** A: Not directly. Listen to worker completion events and schedule the next worker programmatically. --- ## Examples For examples of how to use built-in workers, please refer to the main demo application (`composeApp`). --- ## Support For questions or issues: - GitHub: https://github.com/brewkits/kmpworkmanager - Email: datacenter111@gmail.com --- ## License Copyright © 2024 Nguyễn Tuấn Việt at Brewkits Built with ❤️ for the KMP community