Skip to content

Releases: brewkits/native_workmanager

v1.2.7 - Enforce DartWorker timeoutMs end-to-end (Issue #30)

11 May 16:20
866ca89

Choose a tag to compare

Fixed

  • Core: Enforced DartWorker.timeoutMs end-to-end (Issue #30).
    • Android and iOS bridges now correctly forward timeoutMs to the Dart callback dispatcher.
    • Added resolveDispatcherTimeout helper in Dart to securely parse the timeout, protecting against NaN, Infinity, and invalid types.
    • Enforced timeoutMs in both the background dispatcher and the foreground MethodChannel (_executeDartCallback).
    • Added comprehensive unit, integration, performance, and security test coverage.

v1.2.6: Industrial Stability & Foreground Service (FGS) Bypass

09 May 03:52

Choose a tag to compare

🚀 Version 1.2.6: Industrial-Grade Background Reliability

This release marks a major milestone in production stability, introducing industrial-grade mechanisms to bypass modern OS background restrictions and eliminating critical concurrency issues.

🌟 Key Highlights

🛡️ Android: Industrial Foreground Service (FGS) Bypass

Run heavy, long-running tasks without being killed by Android 12+ battery optimizations.

  • Priority Execution: Promotes tasks to system-level Foreground Services.
  • Custom Notifications: Full control over title, body, and action buttons (e.g., 'Stop Sync').
  • Android 14 Ready: Automatically handles FGS types and required system permissions.

⚡ Android: Expedited Work (Locked Device Support)

Resolved the regression where tasks wouldn't fire when the screen was locked.

  • Doze Mode Bypassing: Maps allowWhileIdle: true to WorkManager's Expedited Work.
  • Instant Triggering: Tasks fire reliably even on idle or locked devices.

💎 iOS: Swift Concurrency & Deadlock Fixes

  • Serial Database Queues: Migrated all SQLite stores to serial queues to eliminate deadlocks.
  • Scheduling Reliability: Integrated micro-delays for complex TaskGraph chains to ensure BGTaskScheduler registration success.

🛠 Full Change Log

Added

  • Android: Industrial-grade Foreground Service support via ForegroundNotificationConfig.
  • Android: Proactive task promotion using setForeground() for immediate background execution.
  • Android: Full compliance with Android 14 (API 34) Foreground Service Types.
  • Example: New 'FGS Bypass' demo page showing real-time priority task execution.

Fixed

  • Android: Fixed regression where background tasks would not fire on locked devices (#28).
  • iOS: Fixed Swift Concurrency deadlocks affecting OfflineQueue and TaskGraph.
  • iOS: Synchronized background task identifiers between Swift code and setup scripts.
  • Test: Platform-aware integration test suite with automatic iOS Simulator isolation.

Documentation

  • Architecture: Deep-dive into v1.2.6 reliability enhancements in ARCHITECTURE_ANALYSIS.md.
  • Concurrency: Defined new serial-queue threading contract in CONCURRENCY.md.
  • Migration: Step-by-step guide for using new FGS features.

📦 Installation

Update your pubspec.yaml:

dependencies:
  native_workmanager: ^1.2.6

Note for Android: To ensure tasks survive app kills, please ensure your Application class implements Configuration.Provider as described in our Android Setup Guide.

v1.2.5

06 May 18:26
87b56bb

Choose a tag to compare

What's changed

Fixed

  • Core — Issue #26: Removed over-restrictive assert in TaskTrigger.periodic that rejected initialDelay + runImmediately: false. Both parameters are independently valid; the OS handles them correctly. Previously threw AssertionError at runtime.
  • iOS: runImmediately flag was being silently recomputed from initialDelay in KMPSchedulerBridge.swift instead of using the caller-provided value. The bridge now passes through the exact value set by the caller.

What initialDelay + runImmediately: false means

await NativeWorkManager.enqueue(
  taskId: 'hourly-sync',
  trigger: TaskTrigger.periodic(
    const Duration(hours: 1),
    initialDelay: const Duration(minutes: 30), // first execution starts after 30 min
    runImmediately: false,                      // first period is a full interval
  ),
  worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
);

Both parameters can be combined freely. The OS decides the exact scheduling within the declared window.


Also in this release

  • iOS logging: 148 bare print() calls across 9 worker files replaced with NativeLogger.d/w/e() — debug output is now gated behind initialize(debugMode: true) in production builds.
  • iOS source: Removed internal sprint/T3 planning comments from HttpDownloadWorker.swift and HttpUploadWorker.swift.
  • README: iOS badge corrected to iOS 14.0+ (matches podspec deployment target); install snippet updated to ^1.2.5.
  • Tests: Added periodic_trigger_reproduction_test.dart — 8-case integration regression suite covering all TaskTrigger.periodic parameter combinations, including the Issue #26 regression.
  • native_workmanager_gen: Version synced to 1.2.5; README install version updated to ^1.2.5; CHANGELOG reformatted to Keep-a-Changelog standard.

Migration

No breaking changes. Update your pubspec.yaml:

dependencies:
  native_workmanager: ^1.2.5

If you use the code generator:

dev_dependencies:
  native_workmanager_gen: ^1.2.5

Full changelog: CHANGELOG.md
KMP core: kmpworkmanager v2.4.3

v1.2.4

02 May 04:18

Choose a tag to compare

What's Changed

Fixed

  • Android: Added automatic ProGuard rules to prevent task classes from being stripped in Release builds (#24).
  • Android: Clarified that Application class setup is required for all tasks to survive app kill.
  • iOS: Synchronized background task identifiers between setup_ios.dart and Swift code.
  • iOS: getTaskStatus() now correctly returns TaskStatus.completed for finished tasks. Previously, the iOS plugin wrote "success" to SQLite but Dart's TaskStatus enum has no success case, so every call returned null.
  • Android: Removed duplicate taskStore.updateStatus() call on task completion. The redundant second write used JSONObject(map).toString() which could corrupt nested result maps.
  • iOS: FlutterEngineManager now disposes the engine after a Dart callback timeout. Previously the engine remained stuck in isInitialized = true, causing all subsequent DartCallbackWorker tasks to silently fail.

Changed

  • Engine: Upgraded core kmpworkmanager to v2.4.3 on both Android (Maven) and iOS (xcframework).

Tests

  • Unit test suite expanded from 898 to 1799 tests.

Upgrading

dependencies:
  native_workmanager: ^1.2.4

Full Changelog: v1.2.3...v1.2.4

v1.2.3 (Security & Stability Fixes)

24 Apr 16:45

Choose a tag to compare

[1.2.3] - 2026-04-24

Critical Security & Stability Fixes

  • iOS: Fix URLSession Background File Loss: Addressed a critical bug where BackgroundSessionManager dispatched file movement asynchronously, causing the OS to delete the downloaded file before the move completed. Implemented a blocking copy before the delegate returns to ensure 100% file retention.
  • iOS: Fix BGTaskScheduler Starvation & Race Condition: Replaced singleton-based completion flag with TaskCompletionGuard tied to the individual BGTask's lifecycle. Replaced peek/remove queue logic with popNextPendingTask() inside queue.sync. This prevents multiple tasks stepping on each other and avoids OS penalties due to uncompleted background tasks.
  • iOS: Fix Missing I/O Interruption on Cancel: The iOS cancellation handler now correctly invokes worker.stop() (which calls URLSessionTask.cancel()), immediately dropping mid-flight network connections instead of waiting for the next cooperative checkpoint.
  • Android: Bypass 10KB WorkManager Hard Limit: WorkManager throws an exception if the input data payload exceeds 10KB. The plugin now gracefully handles oversized payloads by spilling them to a secure .json file (wm_spill_*.json) in the cache directory, passing only the file URI to WorkManager, and seamlessly re-hydrating the payload in BaseKmpWorker.
  • Android: Fix OfflineQueueProcessor Infinite Loop: Catch-block in the offline queue loop now properly deletes corrupted entries that throw during enqueue (like oversized payloads prior to the spill fix). This prevents the processor from getting stuck in an infinite retry loop that drains the battery.
  • 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.

Added

  • Feature: Support initialDelay and runImmediately for periodic tasks (#21)
    • Allows delaying the first execution of a periodic task.
    • Added runImmediately flag to skip the first execution.
    • On Android, uses native PeriodicWorkRequest.setInitialDelay().
    • On iOS, maps initialDelay to earliestBeginDate for optimized scheduling.
    • Added parameters to TaskTrigger.periodic().
  • Enterprise-Grade Testing:
    • Implemented comprehensive scripts/run_all_tests.sh covering Unit, Integration, Security, Performance, and Stress tests.
    • Added specific performance benchmarks for task scheduling overhead.
    • Added malicious payload protection tests.
  • Improved CI/CD: Integrated automated Security, Performance, and Stress testing into the GitHub Actions pipeline.

Fixed (General)

  • Android: Upgraded to kmpworkmanager 2.4.1
    • Switched to native setInitialDelay instead of manual bypass logic.
    • Fixed edge-case crashes on Android 15.
  • iOS: Improved Periodic Task Lifecycle
    • Fixed regression where periodic tasks were not tracked in activeTasks, preventing cancellation.
  • Android: Fixed broken expedited flag logic in direct enqueue path.

v1.2.2: Plugin Registration, iOS Window & Android Middleware Fixes

20 Apr 16:28
98181de

Choose a tag to compare

Added

  • registerPlugins parameter in NativeWorkManager.initialize(): opt-in flag to register all Flutter plugins in the background engine, required when using plugins like flutter_local_notifications inside DartWorker callbacks. Defaults to false to preserve the Zero-Engine I/O principle and avoid side-effects (e.g. Bluetooth disconnects). Also added NativeWorkmanagerPlugin.setPluginRegistrantCallback on Android and iOS to allow selective plugin registration when registerPlugins is false. (#18)

Fixed

  • iOS: openFile always fails on Flutter 3.38+ / scene-based appsUIApplication.shared.keyWindow returns nil in UIWindowScene lifecycle. Replaced with a new activeRootViewController extension that traverses connectedScenes to find the active key window. (#16)
  • Android: StackOverflowError when middleware is registered — Kotlin companion extension applyMiddleware was shadowing the internal package-level function of the same name, causing infinite recursion. Renamed the internal function to applyMiddlewareInternal to eliminate the ambiguity. (#17)
  • native_workmanager_gen incompatible with Flutter 3.41.xanalyzer >=11.0.0 requires meta ^1.18.0 which conflicts with the Flutter SDK's meta 1.17.0 pin. Widened constraint to >=10.0.0 <13.0.0; analyzer 10.x supports all APIs used by the generator and requires only meta ^1.15.0. (#15)

v1.2.1 - Security Hardening & Test Suite Enhancements

19 Apr 23:46

Choose a tag to compare

Security Hardening

  • HTTPS Enforcement: Option to block plain HTTP requests.
  • SSRF Protection: Option to block private/loopback IP ranges.
  • Path Traversal: Enhanced validation against null-byte and encoded dot-segment injection.

Test Suite Enhancements

  • 100+ new test cases across unit, security, and integration categories.
  • Multi-stage workflow verification (Download -> Encrypt -> Dart Finalizer).
  • High-load stress testing (30+ concurrent tasks).

Native Improvements

  • Android: Hard isolate timeouts to prevent memory leaks.
  • Android: Batch deletion for task store maintenance.
  • Observability: New type-safe WorkManagerLogger interface.

Fixed

  • Migration Tool: Fixed missing executable for dart run native_workmanager:migrate (#14).

v1.2.0

17 Apr 05:50
987362f

Choose a tag to compare

What's new in v1.2.0

Added

  • Android cold-start DartWorker persistence: DartWorker tasks now execute reliably after app kill. The callbackHandle is persisted to SharedPreferences (Android) and UserDefaults (iOS) during initialize() and automatically restored when WorkManager restarts the process. Requires host app to implement Configuration.Provider — see Android Setup Guide.
  • Advanced Remote Trigger: Support for direct commands in push payloads (native_wm key). Execute tasks, chains (enqueue_chain), graphs, and offline queues without waking Flutter.
  • HMAC Security: HMAC SHA-256 signature verification for remote triggers to prevent unauthorized task execution.
  • Real-time Observability: DevTools extension real-time event streaming via developer.postEvent.
  • Global Middleware API: HeaderMiddleware, RemoteConfigMiddleware, LoggingMiddleware.
  • Task Graphs (DAG): Arbitrary dependency graphs on Android with DFS cycle detection.
  • Code Generation: native_workmanager_gen generates type-safe callback IDs and worker registries from @WorkerCallback annotations.

See CHANGELOG for full details.

v1.1.2 — TaskHandler, BLE fix, FakeWorkManager fix

14 Apr 05:04
3c3f1ab

Choose a tag to compare

What's New

New: TaskHandler — per-task progress and result

enqueue() now returns a TaskHandler instead of a raw ScheduleResult:

final handler = await NativeWorkManager.enqueue(
  taskId: 'download',
  worker: NativeWorker.httpDownload(url: url, savePath: '/tmp/video.mp4'),
);

// Stream progress for this task only
handler.progress.listen((p) {
  print('${p.progress}% — ${p.networkSpeedHuman} — ETA ${p.timeRemainingHuman}');
});

// Await completion
final result = await handler.result;

Or use the built-in widget:

TaskProgressCard(handler: handler, title: 'Downloading video')

Fix: BLE disconnect when using DartWorker (issue #6)

The secondary Flutter engine used for DartWorker tasks was created with automaticallyRegisterPlugins = true, which registered all host-app plugins (including Bluetooth plugins) in the background engine. When the engine was destroyed after the task, it called onDetachedFromEngine() on those plugins — closing the shared BluetoothManager connection and dropping active BLE connections.

Fix: The engine is now created with automaticallyRegisterPlugins = false. Only the callback dispatcher needed for DartWorker is registered explicitly. All built-in native workers (HTTP, file, image, crypto, etc.) were already unaffected.

Thanks to @zhujianguo for identifying and reporting the root cause.

Fix: FakeWorkManager.enqueueResultByTaskId stub was broken

FakeWorkManager.enqueue() was hardcoded to return ScheduleResult.accepted after the TaskHandler refactor, ignoring enqueueResult and enqueueResultByTaskId. Fixed — stubs now work correctly for unit tests.

Breaking Changes

enqueue() and enqueueAll() now return TaskHandler / List<TaskHandler> instead of ScheduleResult / List<ScheduleResult>. Access handler.scheduleResult for the old value.

Migration

// Before
final result = await NativeWorkManager.enqueue(...);
if (result == ScheduleResult.accepted) { ... }

// After
final handler = await NativeWorkManager.enqueue(...);
if (handler.scheduleResult == ScheduleResult.accepted) { ... }

v1.1.1 Update core & Fix bugs

11 Apr 12:46
601bd91

Choose a tag to compare

What's Changed

Added

  • Token Refresh on 401: HttpRequestWorker and HttpSyncWorker now support automatic token refresh via TokenRefreshConfig when the server responds with 401
  • Response Validation Patterns: configurable response validation for HTTP workers
  • http_sync_test.dart: integration test suite for HttpSyncWorker

Changed

  • XCFramework simulator slice updated to support arm64 + x86_64
  • kmpworkmanager engine upgraded to 2.3.9 (fixes InvalidForegroundServiceTypeException on Android 16 for heavy tasks)
  • Workers refactored to use the new WorkerEnvironment parameter signature from kmpworkmanager 2.3.8+

Fixed

  • FlutterEngineManager.dispose() now always resets isInitialized and nulls the engine reference even when engine.destroy() throws (fixes chain resume and DartWorker constraint tests after memory pressure events)
  • pause method correctly routed on Android

Full Changelog: v1.1.0...v1.1.1