Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [1.4.2] - 2026-07-17

### Fixed

- **iOS SwiftPM resolution failed one layer above the 1.4.1 fix — Issue #52.**
Flutter's generated `FlutterGeneratedPluginSwiftPackage` references every
plugin by the **hyphenated** library product name
(`plugin.name.replaceAll('_', '-')` in `flutter_tools` — SwiftPM uses the
product name as `CFBundleIdentifier` when linking dynamically, and bundle
identifiers cannot contain underscores). `Package.swift` exported the product
as `native_workmanager`, so SPM-enabled apps failed dependency resolution with
*"product 'native-workmanager' … not found in package 'native_workmanager'"*.
The library product is now `native-workmanager`; the package and target names
keep their underscores. Thanks @zaqwery for the precise diagnosis — again.

- **iOS: two workers did not compile under SwiftPM.** `CryptoWorker` and
`FileSystemWorker` use `UIApplication` (background-task API) without an
explicit `import 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-manager` depending 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
Expand Down
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,16 @@ number, even when a release has no codegen changes.
4. `flutter analyze` (0 issues) and the full test suite
(`./scripts/run_all_tests.sh`, plus `flutter build ios --simulator` and an Android APK
build as a smoke test) must be clean before tagging.
**Also build a scratch Flutter app with SwiftPM enabled** — both #49 and #52 escaped
because the plugin was only built in isolation or via CocoaPods:
`flutter create /tmp/spmtest && cd /tmp/spmtest` → add this plugin as a `path:`
dependency → raise the app's `IPHONEOS_DEPLOYMENT_TARGET` to 14.0 →
`flutter config --enable-swift-package-manager && flutter build ios --simulator --debug`
(restore with `flutter config --no-enable-swift-package-manager` after). This exercises
Flutter's generated SwiftPM manifest, which references the plugin by the **hyphenated**
library product name (`native-workmanager`) — that product name in `Package.swift` must
never be renamed back to underscores, and iOS sources must compile under SPM's stricter
module isolation (no transitive `import UIKit`).
5. Commit, tag `vX.Y.Z`, push.
6. Cut a GitHub release on the tag. If the iOS xcframework changed, attach
`KMPWorkManager.xcframework.zip` as a release asset **named exactly that** — GitHub's
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ No boilerplate. No native code to write. No `AndroidManifest.xml` changes. Each

```yaml
dependencies:
native_workmanager: ^1.4.1
native_workmanager: ^1.4.2
```

**2. Initialize once in `main()`:**
Expand Down
2 changes: 1 addition & 1 deletion doc/ANDROID_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Add to your `pubspec.yaml`:

```yaml
dependencies:
native_workmanager: ^1.4.1
native_workmanager: ^1.4.2
```

Run:
Expand Down
2 changes: 1 addition & 1 deletion doc/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Or manually:

```yaml
dependencies:
native_workmanager: ^1.4.1
native_workmanager: ^1.4.2
```

Then run:
Expand Down
4 changes: 2 additions & 2 deletions doc/MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ dependencies:
**After:**
```yaml
dependencies:
native_workmanager: ^1.4.1
native_workmanager: ^1.4.2
```

**Then run:**
Expand Down Expand Up @@ -865,7 +865,7 @@ Use this checklist to track your migration progress:
```yaml
dependencies:
workmanager: ^0.5.0
native_workmanager: ^1.4.1
native_workmanager: ^1.4.2
```

Migrate tasks one at a time, then remove workmanager when done.
Expand Down
2 changes: 1 addition & 1 deletion doc/MIGRATION_TOOL_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Updated dependencies file:
dependencies:
flutter:
sdk: flutter
native_workmanager: ^1.4.1 # Replaced workmanager
native_workmanager: ^1.4.2 # Replaced workmanager
```

**Usage:**
Expand Down
14 changes: 11 additions & 3 deletions example/integration_test/device_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ Future<bool> _ditRetryCounter(Map<String, dynamic>? input) async {
final path = input?['counterFile'] as String?;
if (path != null) {
final f = File(path);
final n = f.existsSync() ? (int.tryParse(f.readAsStringSync().trim()) ?? 0) : 0;
final n = f.existsSync()
? (int.tryParse(f.readAsStringSync().trim()) ?? 0)
: 0;
f.writeAsStringSync('${n + 1}');
print('[DartWorker] dit_retry_counter run #${n + 1}');
}
Expand Down Expand Up @@ -2960,7 +2962,10 @@ void main() {
}
});

final eventFuture = _waitEvent(id, timeout: const Duration(seconds: 45));
final eventFuture = _waitEvent(
id,
timeout: const Duration(seconds: 45),
);

await NativeWorkManager.enqueue(
taskId: id,
Expand Down Expand Up @@ -2996,7 +3001,10 @@ void main() {
'issue_39: DartWorker status becomes terminal in allTasks() after success',
(tester) async {
final id = _id('issue39_status');
final eventFuture = _waitEvent(id, timeout: const Duration(seconds: 45));
final eventFuture = _waitEvent(
id,
timeout: const Duration(seconds: 45),
);

await NativeWorkManager.enqueue(
taskId: id,
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.4.1"
version: "1.4.2"
objective_c:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion ios/native_workmanager.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'native_workmanager'
s.version = '1.4.1'
s.version = '1.4.2'
s.summary = 'Background task manager for Flutter using platform-native APIs.'
s.description = <<-DESC
Native WorkManager is a Flutter plugin that provides native background task scheduling
Expand Down
9 changes: 8 additions & 1 deletion ios/native_workmanager/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ let package = Package(
.iOS("14.0"),
],
products: [
.library(name: "native_workmanager", targets: ["native_workmanager"]),
// The library PRODUCT must be the hyphenated plugin name (issue #52).
// Flutter's generated FlutterGeneratedPluginSwiftPackage references
// plugins as .product(name: plugin.name.replaceAll('_', '-'),
// package: plugin.name) — see flutter_tools swift_package_manager.dart.
// SPM uses the product name as CFBundleIdentifier when linked
// dynamically, and CFBundleIdentifier cannot contain underscores.
// Package name and target names keep their underscores.
.library(name: "native-workmanager", targets: ["native_workmanager"]),
],
dependencies: [
// No third-party dependencies. Uses Apple Archive for ZIP operations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import Foundation
import KMPWorkManager
import CryptoKit
import CommonCrypto
// Explicit UIKit import: required for UIApplication (background task API).
// CocoaPods builds compiled without it because another module re-exported
// UIKit; SwiftPM builds do not (issue #52 E2E verification caught this).
import UIKit

/// Native cryptographic operations worker for iOS.
///
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import Foundation
import KMPWorkManager
// Explicit UIKit import: required for UIApplication (background task API).
// CocoaPods builds compiled without it because another module re-exported
// UIKit; SwiftPM builds do not (issue #52 E2E verification caught this).
import UIKit

/// Built-in worker: File system operations
///
Expand Down
8 changes: 8 additions & 0 deletions native_workmanager_gen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

---

## [1.4.2] - 2026-07-17

- Version bump synchronized with `native_workmanager` 1.4.2. No codegen changes — the
1.4.2 fixes (iOS SwiftPM hyphenated library product #52, explicit UIKit imports)
are entirely in the main package's iOS build configuration and Swift sources.

---

## [1.4.1] - 2026-07-16

- Version bump synchronized with `native_workmanager` 1.4.1. No codegen changes — the
Expand Down
2 changes: 1 addition & 1 deletion native_workmanager_gen/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: native_workmanager_gen
version: 1.4.1
version: 1.4.2
description: >
Code generator for native_workmanager.
Generates type-safe DartWorker callback IDs and worker registry from
Expand Down
20 changes: 12 additions & 8 deletions native_workmanager_gen/test/worker_callback_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ class WorkerCallback {
/// Builds the asset map for a `lib/workers.dart` source, plus the virtual
/// `native_workmanager` annotation package every fixture imports.
Map<String, String> assets(String workersSource) => {
'native_workmanager|lib/src/worker_callback_generator_annotation.dart':
_annotationSource,
'a|lib/workers.dart': '$_import\n\n$workersSource',
};
'native_workmanager|lib/src/worker_callback_generator_annotation.dart':
_annotationSource,
'a|lib/workers.dart': '$_import\n\n$workersSource',
};

const outputAsset = 'a|lib/workers.worker_callback.g.part';

Expand Down Expand Up @@ -201,7 +201,8 @@ Future<bool> syncContacts(Map<String, dynamic>? input) async => true;
expect(
logs,
anyElement(contains(messageContains)),
reason: 'Expected a SEVERE log containing "$messageContains", '
reason:
'Expected a SEVERE log containing "$messageContains", '
'got: $logs',
);
}
Expand Down Expand Up @@ -250,12 +251,15 @@ Future<bool> twoParams(Map<String, dynamic>? input, String extra) async => true;
''', messageContains: 'has 2 parameters');
});

test('rejects a parameter type that is not Map<String, dynamic>?', () async {
await expectRejected('''
test(
'rejects a parameter type that is not Map<String, dynamic>?',
() async {
await expectRejected('''
@WorkerCallback('bad')
Future<bool> wrongParamType(String? input) async => true;
''', messageContains: 'Map<String, dynamic>?');
});
},
);

test('rejects an empty id', () async {
await expectRejected('''
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: native_workmanager
description: "Background task scheduling for Flutter — 25+ native workers (HTTP, image, crypto, file), task chains, zero Flutter Engine overhead."
version: 1.4.1
version: 1.4.2
homepage: https://github.com/brewkits/native_workmanager
repository: https://github.com/brewkits/native_workmanager
issue_tracker: https://github.com/brewkits/native_workmanager/issues
Expand Down
Loading