native_workmanager 1.4.3
native_workmanager: ^1.4.3 copied to clipboard
Background task scheduling for Flutter — 25+ native workers (HTTP, image, crypto, file), task chains, zero Flutter Engine overhead.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.4.3 - 2026-07-17 #
Fixed #
-
iOS SwiftPM: manifest rejected by stricter SwiftPM toolchains.
Package.swiftdeclared a test target withpath: "../Tests", which escapes the package root. Newer SwiftPM (Xcode 26.x) tolerates the escape, but stricter toolchains reject the entire manifest at load time with "target 'NativeWorkManagerTests' in package 'native_workmanager' is outside the package root" — which breaks dependency resolution for every SPM-enabled consumer app on those toolchains, the same failure mode as #49/#52. The test target is removed from the consumer-facing manifest (nothing ever executed it — no workflow or script invokesswift test— so no coverage is lost; the Swift test sources remain in the repo). Root cause confirmed by a controlled experiment: the CI job that reproduced the failure on the stricter toolchain goes green with the target removed.This closes the last known gap in the SwiftPM install path. All four layers are now verified automatically on every PR: remote binary target resolution, hyphenated product-name resolution, a full
flutter buildof a consuming app with SwiftPM enabled, and compile under SPM's strict module isolation.
1.4.2 - 2026-07-17 #
Fixed #
-
iOS SwiftPM resolution failed one layer above the 1.4.1 fix — Issue #52. Flutter's generated
FlutterGeneratedPluginSwiftPackagereferences every plugin by the hyphenated library product name (plugin.name.replaceAll('_', '-')influtter_tools— SwiftPM uses the product name asCFBundleIdentifierwhen linking dynamically, and bundle identifiers cannot contain underscores).Package.swiftexported the product asnative_workmanager, so SPM-enabled apps failed dependency resolution with "product 'native-workmanager' … not found in package 'native_workmanager'". The library product is nownative-workmanager; the package and target names keep their underscores. Thanks @zaqwery for the precise diagnosis — again. -
iOS: two workers did not compile under SwiftPM.
CryptoWorkerandFileSystemWorkeruseUIApplication(background-task API) without an explicitimport UIKit; CocoaPods builds compiled anyway via transitive module re-export, SwiftPM builds do not. Surfaced by the new end-to-end verification below; explicit imports added.
Changed #
- Release verification now includes a real SwiftPM app build. Both #49 and
#52 escaped because the plugin package was only ever built in isolation or
consumed via CocoaPods. The SPM check now builds a scratch Flutter app with
--enable-swift-package-managerdepending on the plugin, which exercises Flutter's generated manifest (hyphenated product reference, platform minimums, full plugin compile under SPM).
1.4.1 - 2026-07-16 #
Fixed #
-
iOS Swift Package Manager builds failed for pub.dev consumers — Issue #49.
Package.swiftdeclaredKMPWorkManageras a local.binaryTarget(path: "../Frameworks/KMPWorkManager.xcframework"), but that xcframework is stripped from the published package by.pubignoreand only re-created by the CocoaPodsprepare_commandat install time. SwiftPM has no equivalent install hook, so with Flutter's SwiftPM integration enabled the local binary target resolved to nothing andxcodebuildaborted with "local binary target 'KMPWorkManager' … does not contain a binary artifact" — and because Flutter routes a plugin through SwiftPM whenever aPackage.swiftexists (excluding it from CocoaPods), there was no fallback. Replaced the local target with a remote, checksummed.binaryTargetpointing at the same GitHub-release zip the podspec already downloads, so SwiftPM fetches the identical versioned artifact CocoaPods does. Verified end-to-end: SwiftPM downloads the asset, validates the checksum, and builds. Thanks @zaqwery for the precise root-cause report. -
CancellationExceptionswallowed by generic exception handling in 11 Android workers. A worker cancelled mid-run (user callscancel()/cancelAll(), or WorkManager stops the worker because constraints are no longer met) could have itsCancellationExceptioncaught by the worker's owncatch (e: Exception)and converted into a normalWorkerResult.Failure— inHttpDownloadWorker's case withshouldRetry: true, meaning a task the user explicitly cancelled could reschedule itself.ForegroundNativeWorkerwas the most exposed case: it bypassesBaseKmpWorker, so nothing else catches cancellation correctly for the FGS-bypass path. Fixed by addingcatch (e: CancellationException) { throw e }before the generic catch in every worker where the wrapped scope contains a real suspension point (network I/O awaited via child coroutines,delay(),setForeground()). Affected:DbCleanupWorker,FileCompressionWorker,FileDecompressionWorker,FileSystemWorker,ForegroundNativeWorker(two sites),HttpDownloadWorker,HttpRequestWorker,HttpSyncWorker,HttpUploadWorker,ImageProcessWorker,ParallelHttpDownloadWorker(two sites). Five workers audited and confirmed already safe without changes (CryptoWorker,MoveToSharedStorageWorker,ParallelHttpUploadWorker,PdfWorkerrely onBaseKmpWorker's outerCancellationExceptionhandling since they have no local catch around their dispatch;WebSocketWorkeralready usedtry/finallyinstead oftry/catcharound its cancellation-sensitive section). -
Intermittent "Failed host lookup" on Android 15/16. Bumped
androidx.work:work-runtime-ktx2.10.1 → 2.11.2, which fixes an upstream AndroidX WorkManager bug where a backgroundWorkRequestcould start running before the device's network/connectivity state was fully attached, causing spuriousSocketException: Failed host lookupfailures on HTTP calls made from background tasks. Found by auditingflutter_workmanager's issue tracker (still pinned to 2.10.2 at the time of writing, with an open unresolved report) — see issuetracker.google.com/issues/445324855.kmpworkmanagerpulls inwork-runtime-ktx2.9.1 transitively; the directapideclaration here wins Gradle's highest-version resolution (verified:./gradlew :native_workmanager:dependenciesresolves2.9.1 -> 2.11.2).
1.4.0 - 2026-07-16 #
Changed #
- Bumped
kmpworkmanagercore to 3.1.0 (was 3.0.1). 3.1.0 enforcesConstraints.maxRetriesinsideBaseKmpWorker: it reads themaxRetrieskey off the WorkRequest input data and capsFailure(shouldRetry=true)/RetryatN + 1total runs (WorkManager itself has no max-retry API — a rawResult.retry()reschedules forever). The bundled iOSKMPWorkManager.xcframeworkwas rebuilt from 3.1.0.
Fixed #
-
DartWorker
return falsenever retried — permanentResult.failure(). AndroidDartCallbackWorkerand iOS Dart callback paths mapped afalsecallback result toWorkerResult.Failure/.failurewithoutshouldRetry: true. BecauseFailure.shouldRetrydefaults tofalse, WorkManager receivedResult.failure()(reschedule = false) andConstraints.maxRetries/backoffDelayMswere ignored despite docs promising retry-on-false. Native engine/setup exceptions still useshouldRetry = falseso broken engine configuration does not loop forever. -
Android
Constraints.maxRetrieswas silently ignored. Even once a task asked to retry, WorkManager'sResult.retry()is unbounded, so a callback that kept returningfalselooped forever.maxRetriesis now forwarded from the Dart constraints map onto the KMPConstraints(soNativeTaskScheduler-scheduled triggers cap via core) and stamped onto the WorkRequest input data for every direct-enqueue path (one-time, chain, graph) soBaseKmpWorkercan enforce theN + 1ceiling. Periodic work is intentionally excluded — itsrunAttemptCountonly resets on success, so a per-run cap would permanently disable retries after the first cap hit.ForegroundNativeWorker(which maps results itself, bypassingBaseKmpWorker) enforces the same cap inline. iOSRetryConfignow readsmaxRetriesviaNSNumber(MethodChannel integers were silently dropped to0= no retry) and defaults to3to match the Dart contract.
1.3.3 - 2026-07-14 #
Fixed #
-
DartWorker progress events dropped (UI stuck at 0%) — Issue #38. Native emitted the progress map without a
timestamp, so the Dart session-filter (timestamp < _sessionStartTime, defaulting the missing value to0) silently discarded every progress event. AndroidProgressUpdate.toMap()/toJson()and iOSProgressReporter/emitProgressnow stamptimestamp; the Dart filter treats a missing/0timestamp as "current" for backward compatibility with older native builds. Covered byissue_38_*indevice_integration_test.dart.Fixing this on iOS surfaced two further iOS-only gaps (caught by the device test) that PR #40 alone did not close: (a)
__taskIdwas never injected into a foreground DartWorker's input, so the callback had no id to report progress with —executeDartWorkerViaMethodChannelnow merges it in, mirroring Android'sDartCallbackWorker; (b) thedev.brewkits/dart_worker_channelreportProgresshandler existed only on theFlutterEngineManagerbackground engine, so foreground callbacks threwMissingPluginException— the main engine now registers the same handler, routed throughProgressReporter. -
DartWorker TaskStore status stuck on
pendingafter success — Issue #39. Only theTaskEventBuspath persisted terminal status to SQLite, andDartCallbackWorkernever emits on that bus, so completed DartWorkers stayedpendingforever inallTasks(). The WorkInfo fallback inobserveWorkCompletionnow callstaskStore.updateStatus(...)for running/completed/failed/cancelled, plus asyncTaskStoreWithWorkManager()reconciliation on restart to repair rows left stale by process death. Covered byissue_39_*indevice_integration_test.dart.
Security #
- Constant-time HMAC signature comparison in RemoteTrigger. The remote-trigger
HMAC verification compared signatures with plain string equality (Android
String.equals, iOS==), which short-circuits on the first differing byte and can leak — via response timing — how many leading bytes matched (a signature verification timing side-channel). Both platforms now compare the raw HMAC bytes in constant time (AndroidMessageDigest.isEqual, iOS CryptoKitHMAC.isValidAuthenticationCode). Behavior-preserving: canonicalization and HMAC computation are unchanged, so valid signatures still verify and invalid ones are still rejected.
1.3.2 - 2026-07-07 #
Fixed #
- iOS: startup crash on Flutter 3.38+ (UIScene template) — Issue #36.
Apps created with the Flutter 3.38+ iOS template register plugins in
AppDelegate.didInitializeImplicitFlutterEngine, which runs afterapplication(_:didFinishLaunchingWithOptions:)returns. CallingBGTaskScheduler.registerat that point violates Apple's "all launch handlers must be registered before application finishes launching" rule and threwNSInternalInconsistencyExceptionat startup (reported on iPhone 15 / iOS 18.6.2; affects any device on the new template).- BGTask launch handlers are now registered in an ObjC
+loadhook (NWMBGTaskRegistrar) that runs at binary load time — always inside the launch window, on both the old and the new template. Plugin registration only attaches the Swift handlers afterwards. - All
BGTaskScheduler.registercalls now go through ObjC@try/@catch(Swift cannot catchNSException): late or duplicate registration degrades to aBGTASK_REGISTRATION_FAILEDsystem error instead of a crash. - Fixed a latent duplicate-registration crash:
registerHandlers()had no idempotency guard, soGeneratedPluginRegistrantre-running on the headless background engine (FlutterEngineManager) re-registered the identifiers and threw the sameNSInternalInconsistencyException. - BGTasks that fire before the Swift side attaches (cold-start background launch) are buffered and delivered once handlers attach.
- BGTask launch handlers are now registered in an ObjC
Changed #
- kmpworkmanager core upgraded 2.5.1 → 3.0.1 (Android Maven dependency +
bundled iOS XCFramework rebuilt from source).
- v3.0.1 fixes a critical crash on Android 8–11 (API 26–30): expedited tasks
failed with
IllegalStateException: Not implementeddue to a missinggetForegroundInfo()override (regressed in core v2.3.8). - v3.0.0 extracted Ktor HTTP workers into the optional
kmpworkmanager-httpartifact — not needed by this plugin (it ships its own native workers); no API changes affect the plugin bridge.
- v3.0.1 fixes a critical crash on Android 8–11 (API 26–30): expedited tasks
failed with
Added #
- iOS:
NativeWorkmanagerPlugin.registerBGTaskHandlers()— optional explicit registration fromdidFinishLaunchingWithOptions(idempotent, exception-safe). Only needed if a build setup strips ObjC+loadsections. - Example app migrated to the Flutter 3.38+ UIScene template
(
FlutterImplicitEngineDelegate+SceneDelegate) so the device test suite runs on the lifecycle that triggered the crash; newissue_36device regression test asserts handlers are registered in+load, exactly once.
1.3.1 - 2026-06-07 #
Fixed #
- Android (critical regression, since v1.2.4): All file-based native workers
(
HttpDownload,HttpUpload,ParallelHttpDownload/Upload,FileCompression,FileDecompression,ImageProcess,Cryptohash/encrypt/decrypt,Pdf,WebSocket,FileSystem,MoveToSharedStorage) failed on real devices with "Invalid or unsafe file path". v1.2.4 added a blanket"/data"entry toSecurityValidator's blocked-prefix list, which rejected the app's own private sandbox (/data/data/<pkg>,/data/user/<n>/<pkg>— exactly whatpath_providerreturns). The validator now blocks only the genuinely OS-owned sub-directories of/data(/data/local,/data/system,/data/misc,/data/app, …) while allowing the app sandbox. Path-traversal protection (canonical-path resolution) and blocking of/proc,/sys,/etc,/system,/vendor,/dev,/rootare unchanged. AddedSecurityValidatorFilePathTest(Kotlin) plus device coverage in the "All Workers" integration group. - iOS: Fixed an issue where the
KMPWorkManager.xcframeworkwas extracted into a double-nested path (Frameworks/Frameworks/KMPWorkManager.xcframework) duringpod install, causing iOS builds to fail with "Unable to find module dependency: 'KMPWorkManager'". Theprepare_commandinnative_workmanager.podspecis now layout-agnostic (Resolves #33).
1.3.0 - 2026-06-04 #
Added #
-
Android Auto-Init (
NativeWorkManagerInitializer): Plugin now ships anandroidx.startupInitializerdeclared in its ownAndroidManifest.xml. It runs automatically beforeApplication.onCreate(), restoring thecallbackHandlefrom SharedPreferences and initializingKmpWorkManagerwithSimpleAndroidWorkerFactory.- Breaking zero-config change:
DartWorkerkilled-app support now requires no customApplicationclass and no manualAndroidManifest.xmledits for the common case. - Opt-out for apps with custom WorkManager configuration: add
<meta-data android:name="native_workmanager.auto_init" android:value="false" />to<application>in yourAndroidManifest.xml, then followdoc/ANDROID_SETUP.md. isSchedulerInitializedflag prevents double-initialization whenonAttachedToEngineruns after the Initializer.
- Breaking zero-config change:
-
Unified setup CLI (
dart run native_workmanager:setup): Evolvessetup_iosinto a universal command covering both platforms.--android: validates the app manifest has no conflicts with auto-init.--ios: patchesInfo.plistwithUIBackgroundModesandBGTaskSchedulerPermittedIdentifiers(same as the legacysetup_ioscommand).--check: read-only validation mode — no files are written.--help: full usage reference.setup_iosexecutable retained for backward compatibility.
-
iOS
WorkerResult.retry(): Addedretry(reason:delayMs:attemptCap:)factory on the SwiftWorkerResultstruct, providing parity withWorkerResult.Retryintroduced in kmpworkmanager v2.5.0.
Changed #
-
Core: Upgraded KMP WorkManager core dependency from v2.4.3 to v2.5.1.
- Android: added
WorkerResult.Retrybranch inForegroundNativeWorkerto satisfy sealed-class exhaustiveness (maps toResult.retry()). - iOS
KMPWorkManager.xcframeworkrebuilt from v2.5.1 source.
- Android: added
-
iOS retry semantics (
executeWorkerSync): the retry loop now respectsWorkerResult.shouldRetry. A worker returningfailure(shouldRetry: false)stops retrying immediately instead of exhausting allmaxRetriesattempts. -
iOS
maxRetrieshonored on the direct-task execution path:RetryConfig.from(constraintsMap:)is now called and passed toexecuteWorkerSync. PreviouslyConstraints.maxRetrieswas silently ignored on iOS (dead code). -
iOS direct-task
qosnow read fromconstraintsMap["qos"]instead of being hardcoded to"background".
Fixed #
-
Android
DartCallbackWorker:CancellationExceptionis now rethrown before the outercatch (Exception)block.executeDartCallbackis a suspending function; without this fix, WorkManager task cancellation was silently converted to aFailureresult. -
iOS WebSocket:
NativeWorker.webSocket()now throwsUnsupportedErrorat call-site when run on iOS. Previously the task was enqueued and silently failed with "Unknown worker class" becauseIosWorkerFactoryhas noWebSocketWorkercase. -
Android
handleResume: constraint JSON parse failure now logs aNativeLogger.wwarning instead of silently falling back to empty constraints (which could cause resumed downloads to ignorerequiresNetwork/requiresCharging). -
Dart
resolveDispatcherTimeout: values ≤ 0 (zero, negative, NaN, ±Infinity) now fall back to the 25 s default. ADuration(milliseconds: -n).timeout()fires immediately, which would kill every DartWorker. Added four regression tests. -
Android
HttpDownloadWorker— data corruption (directory mode): concurrent downloads to the same directory now each use their own temp file (__pending_<taskId>__.tmp) instead of sharing the hardcoded__pending__.tmp. Two workers writing to the same temp path produced a mixed-byte file; the first to finish would rename corrupted data. -
Android
HttpDownloadWorker— TOCTOU rename (onDuplicate: "rename"): replacedfindNextAvailableFile() + Files.move(REPLACE_EXISTING)with an atomic probe loop usingATOMIC_MOVEonly (noREPLACE_EXISTING). AFileAlreadyExistsExceptionnow signals the next candidate rather than silently overwriting a file from a concurrent download. -
Android constraint conflict warning: enqueueing with
allowWhileIdle: trueandisHeavyTask: truesimultaneously now logs aNativeLogger.wat enqueue time. The long-running worker already bypasses Doze mode, makingallowWhileIdleredundant and potentially causing WorkManager rejection on some Android versions.
1.2.8 - 2026-06-04 #
Changed #
- Core: Upgraded KMP WorkManager core dependency from v2.4.3 to v2.5.1.
- Android: added
WorkerResult.Retrybranch inForegroundNativeWorkerto satisfy sealed-class exhaustiveness (maps toResult.retry()). - iOS: added
WorkerResult.retry(reason:delayMs:attemptCap:)factory method for parity with the new KMP sealed variant; existingfailure(shouldRetry: true)callers unchanged. - iOS
KMPWorkManager.xcframeworkrebuilt from v2.5.1 source.
- Android: added
1.2.7 - 2026-05-11 #
Fixed #
- Core: Enforced
DartWorker.timeoutMsend-to-end (Issue #30).- Android and iOS bridges now correctly forward
timeoutMsto the Dart callback dispatcher. - Added
resolveDispatcherTimeouthelper in Dart to securely parse the timeout, protecting againstNaN,Infinity, and invalid types. - Enforced
timeoutMsin both the background dispatcher and the foregroundMethodChannel(_executeDartCallback). - Added comprehensive unit, integration, performance, and security test coverage.
- Android and iOS bridges now correctly forward
1.2.6 - 2026-05-08 #
Added #
- Android: Industrial-grade Foreground Service (FGS) Support. Added
ForegroundNotificationConfigtoConstraints, allowing tasks to run as prioritized Foreground Services to bypass Android 12+ background restrictions. - Android: Full compliance with Android 14 (API 34) Foreground Service Types. Automatically maps task types (dataSync, location, media, etc.) to system-level flags.
- Android: Proactive task promotion using
setForeground()to ensure immediate execution even when the app is in the background. - Android: FGS state persistence: configuration is automatically restored after device reboots or task resumes.
- Core: Added comprehensive unit tests and a new Demo page in the example app for FGS bypass.
Fixed #
- Android: Fixed regression where background tasks would not fire when the device screen was locked (Doze mode) even after the app was killed. Resolved by correctly mapping
allowWhileIdleto WorkManager's expedited mode (#28). - iOS: Fixed Swift Concurrency deadlocks by migrating SQLite queues (DispatchQueue) from concurrent to serial.
- iOS: Improved scheduling reliability by adjusting internal
TaskTriggerexecution delays on iOS to ensureBGTaskSchedulercorrectly enqueues tasks. - Test: Added platform-aware timeouts for iOS integration tests and automatically excluded timeout-prone integration tests (
TaskGraphandOfflineQueue) when running on the iOS Simulator.
1.2.5 - 2026-05-06 #
1.2.4 - 2026-04-29 #
Fixed #
- Android: Added automatic ProGuard rules to prevent task classes from being stripped in Release builds (#24).
- Android: Clarified that
Applicationclass setup is required for all tasks to survive app kill. - iOS: Synchronized background task identifiers between
setup_ios.dartand Swift code. - iOS:
getTaskStatus()now correctly returnsTaskStatus.completedfor finished tasks. Previously, the iOS plugin wrote"success"to SQLite but Dart'sTaskStatusenum has nosuccesscase, so every call returnednull. - Android: Removed duplicate
taskStore.updateStatus()call on task completion. The redundant second write usedJSONObject(map).toString()which could corrupt nested result maps, overwriting the correctly-encoded first write. - iOS:
FlutterEngineManagernow disposes the engine after a Dart callback timeout. Previously the engine remainedisInitialized = truewith a hungMethodChannel, causing all subsequentDartCallbackWorkertasks to silently fail (timeout again).
Changed #
- Engine: Upgraded core
kmpworkmanagerto v2.4.3 (re-publish of v2.4.2 to fix Maven Central artifact issue; no code changes).
1.2.3 - 2026-04-24 #
Added #
- Feature: Support
initialDelayandrunImmediatelyfor periodic tasks (#21)- Allows delaying the first execution of a periodic task.
- Added
runImmediatelyflag to skip the first execution. - On Android, uses native
PeriodicWorkRequest.setInitialDelay(). - On iOS, maps
initialDelaytoearliestBeginDatefor optimized scheduling. - Added parameters to
TaskTrigger.periodic().
- Security: Advanced Input Validation
- All native workers now perform strict validation to block Null Byte Injection, Path Traversal (
..,%2e%2e), and Shell Injection characters in URLs and file paths.
- All native workers now perform strict validation to block Null Byte Injection, Path Traversal (
- Enterprise-Grade Testing:
- Implemented comprehensive
scripts/run_all_tests.shcovering Unit, Integration, Security, Performance, and Stress tests. - Added specific performance benchmarks for task scheduling overhead.
- Added malicious payload protection tests.
- Implemented comprehensive
- Improved CI/CD: Integrated automated Security, Performance, and Stress testing into the GitHub Actions pipeline.
Fixed #
- Android: Upgraded to
kmpworkmanager 2.4.1- Switched to native
setInitialDelayinstead of manual bypass logic. - Fixed edge-case crashes on Android 15.
- Switched to native
- iOS: Improved Periodic Task Lifecycle
- Fixed regression where periodic tasks were not tracked in
activeTasks, preventing cancellation.
- Fixed regression where periodic tasks were not tracked in
- Android: Fixed broken
expeditedflag logic in direct enqueue path.
1.2.2 - 2026-04-22 #
Added #
registerPluginsparameter inNativeWorkManager.initialize(): opt-in flag to register all Flutter plugins in the background engine, required when using plugins likeflutter_local_notificationsinsideDartWorkercallbacks. Defaults tofalseto preserve the Zero-Engine I/O principle and avoid side-effects (e.g. Bluetooth disconnects). Also addedNativeWorkmanagerPlugin.setPluginRegistrantCallbackon Android and iOS to allow selective plugin registration whenregisterPluginsis false. (#18)
Fixed #
- iOS:
openFilealways fails on Flutter 3.38+ / scene-based apps —UIApplication.shared.keyWindowreturnsnilinUIWindowScenelifecycle. Replaced with a newactiveRootViewControllerextension that traversesconnectedScenesto find the active key window. (#16) - Android:
StackOverflowErrorwhen middleware is registered — Kotlin companion extensionapplyMiddlewarewas shadowing the internal package-level function of the same name, causing infinite recursion. Renamed the internal function toapplyMiddlewareInternalto eliminate the ambiguity. (#17) native_workmanager_genincompatible with Flutter 3.41.x —analyzer >=11.0.0requiresmeta ^1.18.0which conflicts with the Flutter SDK'smeta 1.17.0pin. Widened constraint to>=10.0.0 <13.0.0;analyzer 10.xsupports all APIs used by the generator and requires onlymeta ^1.15.0. (#15)
1.2.1 - 2026-04-19 #
Added #
- Security Hardening: All HTTP workers now support HTTPS Enforcement and Private IP Blocking (SSRF Protection) via
NativeWorkManager.initialize(enforceHttps: true, blockPrivateIPs: true). - Path Traversal Protection: Enhanced file path validation to block null-byte injection and encoded dot-segments (
%2e%2e) across all native workers. WorkManagerLoggerinterface: A type-safe delegate for forwarding background task events to third-party SDKs like Firebase or Sentry without dynamic reflection.- New Test Suite: Added 100+ new test cases covering input sanitization, security policy enforcement, performance benchmarks for large directory operations, and multi-stage task chains.
Fixed #
- Android: Dart Isolate Timeouts: Implemented hard timeout handling for background Dart execution. If an isolate hangs, the engine is now force-disposed to prevent 50MB+ RAM leaks.
- Android: Task Store Performance: Added batch deletion for task history cleanup to prevent long SQLite write-locks on high-traffic apps.
- Migration Tool: Moved the
migrate.dartscript to thebin/directory and added it to theexecutablessection inpubspec.yamlto resolve theCould not find bin/migrate.darterror when runningdart run native_workmanager:migrate(#14). Also changeddeveloper.logtoprintso the CLI output displays correctly. - Test Infrastructure: Fixed a bug in
TaskEventTrackerwhere it incorrectly resolved on "task started" events instead of terminal completion events, leading to flakey stress tests.
1.2.0 - 2026-04-17 #
Added #
- Android cold-start
DartWorkerpersistence:DartWorkertasks now execute reliably after app kill. ThecallbackHandleis persisted toSharedPreferences(Android) andUserDefaults(iOS) duringinitialize()and automatically restored when WorkManager restarts the process. Requires host app to implementConfiguration.Provider— seedoc/ANDROID_SETUP.md. - Advanced Remote Trigger: Support for direct commands in push payloads (
native_wmkey). Execute tasks, chains (enqueue_chain), graphs, and offline queues without waking Flutter. Both Android and iOS support executing task chains completely in the background. - HMAC Security: Robust HMAC SHA-256 signature verification for remote triggers (supporting nested objects) to prevent unauthorized task execution.
- Real-time Observability: DevTools extension now supports real-time event streaming via
developer.postEvent. - Global Middleware API: Global interceptors for task configuration (Headers, RemoteConfig, Logging).
- Code Generation Enhancements:
native_workmanager_gennow generates type-safe enqueue wrappers and automatic worker registries from@WorkerCallbackannotations. - Task Graphs (DAG): Support for complex non-linear task dependencies on Android.