Skip to content

Commit ebafc5a

Browse files
Merge pull request #2 from brewkits/release/v1.0.5
release: v1.0.5
2 parents 5cfce77 + ad1cbcf commit ebafc5a

78 files changed

Lines changed: 4331 additions & 1997 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

BUG_FIX_VERIFICATION.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
## Bug Report
44

5-
**Reporter:** Abdullah Al-Hasnat
65
**Issue:** IllegalStateException: Not implemented at androidx.work.CoroutineWorker.getForegroundInfo(CoroutineWorker.kt:92)
76
**Severity:** Critical - All Android users affected
87

@@ -139,7 +138,7 @@ Expected behavior on Android with WorkManager 2.10.1+:
139138

140139
| Date | Version | Event |
141140
|------|---------|-------|
142-
| 2026-02-16 | native_workmanager 1.0.3 | Initial bug report from Abdullah |
141+
| 2026-02-16 | native_workmanager 1.0.3 | Initial bug report from community user |
143142
| 2026-02-17 | kmpworkmanager 2.3.3 | Fix released to Maven Central |
144143
| 2026-02-18 | native_workmanager 1.0.4 | Updated dependency, bug verified fixed |
145144

CHANGELOG.md

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,105 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
---
99

10+
## [1.0.5] - 2026-02-22
11+
12+
### Added
13+
14+
- **Swift Package Manager (SPM) support for iOS** — the plugin now works with both SPM and CocoaPods
15+
- Added `ios/native_workmanager/Package.swift` with `binaryTarget` for the bundled KMPWorkManager XCFramework and `ZIPFoundation` dependency
16+
- Moved Swift sources from `ios/Classes/` to `ios/native_workmanager/Sources/native_workmanager/` (Flutter SPM layout)
17+
- Updated `ios/native_workmanager.podspec` to reference new source paths (CocoaPods build unchanged)
18+
- Resolves the partial pub.dev platform score for Swift Package Manager support
19+
20+
### Fixed (Critical — Android periodic tasks)
21+
22+
- **Android: Trigger type was hardcoded to `OneTime` — periodic/exact/windowed triggers were silently ignored** (`NativeWorkmanagerPlugin.kt`)
23+
- **Root cause:** `handleEnqueue` always passed `TaskTrigger.OneTime(initialDelayMs = 0)` to the kmpworkmanager scheduler, regardless of what the Dart side sent
24+
- **Impact:** Every task was treated as a one-shot task. Periodic tasks only ran once; exact and windowed triggers were completely ineffective
25+
- **Fix:** Added full trigger parsing from `call.argument<Map<String, Any?>>("trigger")`, switching on the `"type"` key and creating the correct `TaskTrigger` subtype (`Periodic`, `Exact`, `Windowed`, `ContentUri`, battery, idle, storage)
26+
- **Reported by:** Abdullah Al-Hasnat (confirmed in production)
27+
28+
- **Android: `ExistingTaskPolicy` was hardcoded to `KEEP` — users could not replace existing tasks** (`NativeWorkmanagerPlugin.kt`)
29+
- **Root cause:** `handleEnqueue` always passed `ExistingPolicy.KEEP`, ignoring the `existingPolicy` argument sent from Dart
30+
- **Impact:** `ExistingTaskPolicy.replace` was silently treated as `KEEP`; calling `enqueue()` a second time with the same task ID had no effect even when `replace` was specified
31+
- **Fix:** Parse `existingPolicy` from `call.argument<String>("existingPolicy")` and map `"replace"``ExistingPolicy.REPLACE`, anything else → `ExistingPolicy.KEEP`
32+
33+
- **Android: Constraints were hardcoded to defaults — network, charging, backoff, system constraints were never applied** (`NativeWorkmanagerPlugin.kt`)
34+
- **Root cause:** `handleEnqueue` and `toTaskRequest` always used `Constraints()` (all defaults), ignoring the map sent by Dart
35+
- **Impact:** `requiresNetwork`, `requiresCharging`, `backoffPolicy`, `backoffDelayMs`, `isHeavyTask`, `systemConstraints` were silently ignored
36+
- **Fix:** Added `parseConstraints(map: Map<String, Any?>?)` helper that reads every field from Dart's `Constraints.toMap()` output
37+
38+
- **Android: Periodic tasks stopped emitting events after the first execution** (`NativeWorkmanagerPlugin.kt`)
39+
- **Root cause:** `observeWorkCompletion` used `Flow.first {}` which suspends until the FIRST terminal state (SUCCEEDED/FAILED/CANCELLED) and then unsubscribes. Periodic tasks never reach a terminal state between cycles, so subsequent executions produced no events
40+
- **Fix:** Periodic tasks now use `takeWhile { state != CANCELLED }.collect {}` to observe every execution cycle; one-time tasks keep the original `first {}` behaviour
41+
- **Related:** `isPeriodic` flag propagated from trigger parsing to `observeWorkCompletion`
42+
43+
- **iOS: `flexMs` key lookup used wrong name `flexIntervalMs`** (`KMPSchedulerBridge.swift`)
44+
- **Root cause:** `parseTrigger` looked for `map["flexIntervalMs"]` but Dart's `PeriodicTrigger.toMap()` sends the key as `"flexMs"`
45+
- **Impact:** `flexInterval` was always `nil` on iOS regardless of what Dart passed; WorkManager flex window was completely ignored
46+
- **Fix:** Changed key from `"flexIntervalMs"` to `"flexMs"` to match Dart
47+
48+
- **iOS: Constraints parsing ignored `qos` and `exactAlarmIOSBehavior` from Dart** (`KMPSchedulerBridge.swift`)
49+
- **Root cause:** `parseConstraints` only read `requiresNetwork`, `requiresCharging`, and `isHeavyTask`; `qos` and `exactAlarmIOSBehavior` were hardcoded to `.background` and `.showNotification`
50+
- **Fix:** Added full parsing for `qos` and `exactAlarmIOSBehavior` from the constraint map
51+
52+
- **iOS: Chain resume drops all worker config values** (`NativeWorkmanagerPlugin.swift`)
53+
- **Root cause:** `resumeChain` used `.mapValues { $0.value as? [String:Any] ?? [:] }.compactMapValues { $0.isEmpty ? nil : $0 }` which cast every `AnyCodable` to a nested dict; non-dict values (strings, ints, URLs) became empty dicts and were filtered out
54+
- **Impact:** After an app kill/crash, resumed chains ran workers with an empty config
55+
- **Fix:** Replaced faulty pipeline with `task.workerConfig.mapValues { $0.value }`
56+
57+
- **iOS: Initial task state set to `"running"` instead of `"pending"`** (`NativeWorkmanagerPlugin.swift`)
58+
- **Root cause:** `handleEnqueue` set `taskStates[taskId] = "running"` when scheduling; the task hasn't started executing yet
59+
- **Fix:** Changed to `"pending"` — state transitions to `"running"` when the worker actually starts
60+
61+
- **iOS: Custom worker registration always silently skipped** (`example/ios/Runner/AppDelegate.swift`)
62+
- **Root cause:** `#if canImport(ImageCompressWorker)` is always `false``canImport()` checks module names, not class names
63+
- **Fix:** Removed `#if canImport` guard; registration now unconditional
64+
65+
- **iOS: `IosWorker` protocol and `IosWorkerFactory` were `internal`** (`ios/Classes/workers/IosWorker.swift`)
66+
- **Root cause:** Both lacked `public` modifier, making them inaccessible outside the `native_workmanager` module; host apps could not conform to `IosWorker` or call `IosWorkerFactory.registerWorker`
67+
- **Fix:** Added `public` to both declarations
68+
69+
- **iOS example: `WorkerError` undefined; `WorkerResult.success(data:)` wrong type** (`example/ios/Runner/ImageCompressWorker.swift`)
70+
- **Fix:** Defined local `ImageCompressError` enum; changed `success(data: "string")``success(message: "...", data: [String: Any])`
71+
72+
- **iOS example: Type conflict between `Runner.IosWorker` and `native_workmanager.IosWorker`** (`example/ios/Runner/IosWorker.swift`, `WorkerResult.swift`)
73+
- **Fix:** Replaced duplicate declarations with `typealias` pointing to the plugin module's types
74+
75+
- **Android: Stale version strings in comments and logs** (`NativeWorkmanagerPlugin.kt`, `KMPBridge.swift`)
76+
- **Fix:** Updated "v2.3.1" / "v2.3.0" references to v2.3.3
77+
78+
- **Dart docs: `ExistingTaskPolicy` default incorrectly documented as `keep`**
79+
- **Fix:** Corrected to `replace` (the actual default in `NativeWorkManager.enqueue`)
80+
81+
- **Example app: Stale version strings** — updated to v1.0.5
82+
83+
- **Android: `CancellationException` silently swallowed in Flow-collection coroutines** (`NativeWorkmanagerPlugin.kt`)
84+
- **Root cause:** Three `catch (e: Exception)` blocks in `listenForProgress`, `listenForEvents`, and `observeWorkCompletion` caught `kotlinx.coroutines.CancellationException` (a subtype of `Exception`), preventing structured concurrency from propagating coroutine cancellation
85+
- **Impact:** Coroutines leaked when the Flutter plugin was detached (e.g., hot reload, app restart); could cause event-sink callbacks after disposal
86+
- **Fix:** Added `catch (e: kotlinx.coroutines.CancellationException) { throw e }` before the generic `catch (e: Exception)` block in all three locations
87+
88+
- **Android: HttpDownloadWorker deletes temp file on network error, breaking resume** (`HttpDownloadWorker.kt`)
89+
- **Root cause:** The `catch (e: Exception)` block unconditionally called `tempFile.delete()`, destroying the partial download that resume logic depends on
90+
- **Impact:** Resume downloads (`enableResume = true`) always restarted from byte 0 on any network error, wasting bandwidth
91+
- **Fix:** Removed the unconditional temp-file deletion; the file is now preserved so the next retry can use the `Range: bytes=N-` header to resume
92+
93+
- **iOS: `CryptoWorker` loads arbitrarily large files into RAM before size check** (`CryptoWorker.swift`)
94+
- **Root cause:** `Data(contentsOf: inputURL)` was called before `SecurityValidator.validateFileSize()`, so large files caused an OOM crash rather than a clean error
95+
- **Fix:** Moved `validateFileSize()` guard to run before reading the file; added random salt generation (`SecRandomCopyBytes`) replacing the hardcoded string salt, improving encryption security
96+
97+
- **Android: `ImageProcessWorker` uses `min()` without explicit import** (`ImageProcessWorker.kt`)
98+
- **Root cause:** `min(widthRatio, heightRatio)` without an explicit `import kotlin.math.min` could resolve to `java.lang.Math.min` in some Kotlin compiler configurations, producing a compile warning or error
99+
- **Fix:** Changed to `maxOf(1, min(widthRatio, heightRatio))` using Kotlin's built-in `maxOf`; the `max(1, …)` also prevents `sampleSize = 0` when one image dimension already fits within the requested bounds
100+
101+
### Added
102+
- **Device integration test suite** (`example/integration_test/device_integration_test.dart`)
103+
- Covers all trigger types, ExistingPolicy (REPLACE/KEEP), all 11 workers, chains, tags, cancellation, events and progress streams
104+
- **GROUP 9 — DartWorker constraint & delay enforcement** — reproduces and verifies the fix for issue #1: `requiresNetwork` and `initialDelay` are now correctly applied to the WorkManager `WorkRequest` for all task types
105+
- Run with: `flutter test integration_test/device_integration_test.dart --timeout=none`
106+
107+
---
108+
10109
## [1.0.4] - 2026-02-18
11110

12111
### Fixed
@@ -15,7 +114,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15114
- **Solution:** Upgraded to `kmpworkmanager:2.3.3` which adds proper `getForegroundInfo()` override
16115
- **Impact:** All Android users can now safely use WorkManager 2.10.0+
17116
- **Files changed:** `android/build.gradle`
18-
- **Reported by:** Abdullah Al-Hasnat
19117
- **Example app: Flutter rendering error** in Chain Resilience Test screen
20118
- Fixed `RenderFlex` unbounded height constraint error caused by `Expanded` widget inside `SingleChildScrollView`
21119
- Changed to `SizedBox` with fixed height for logs section
@@ -60,7 +158,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
60158
- **Impact:** All Android users attempting to use the plugin would get runtime errors when tasks execute
61159
- **Solution:** Added `KmpWorkManager.initialize()` call in `initializeKoin()` before setting up Koin modules
62160
- **Files changed:** `NativeWorkmanagerPlugin.kt`
63-
- **Reported by:** Abdullah Al-Hasnat (GitHub issue - user feedback)
161+
- **Reported by:** Community user feedback
64162

65163
### Added
66164
- **Documentation: Android Setup Guide** (`doc/ANDROID_SETUP.md`)
@@ -586,5 +684,5 @@ Built on [kmpworkmanager v2.3.0](https://github.com/pablichjenkov/kmpworkmanager
586684

587685
**Latest Version:** 1.0.1
588686
**Status:** Production Ready - Stable release for all production apps
589-
**KMP Parity:** 100% (kmpworkmanager v2.3.1)
687+
**KMP Parity:** 100% (kmpworkmanager v2.3.3)
590688
**Platforms:** Android | iOS
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2-
package="dev.brewkits.native_workmanager">
2+
xmlns:tools="http://schemas.android.com/tools"
3+
package="dev.brewkits.native_workmanager">
4+
5+
<!-- Required for isHeavyTask=true (KmpHeavyWorker foreground service) -->
6+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
7+
<!-- Required on Android 14+ (API 34+) for typed foreground services -->
8+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
9+
10+
<application>
11+
<!-- Declare foreground service type for WorkManager's SystemForegroundService.
12+
Required on Android 14+ (API 34+) when using isHeavyTask=true.
13+
If your app needs a different type, override with tools:node="replace". -->
14+
<service
15+
android:name="androidx.work.impl.foreground.SystemForegroundService"
16+
android:foregroundServiceType="dataSync"
17+
tools:node="merge" />
18+
</application>
319
</manifest>

0 commit comments

Comments
 (0)