diff --git a/.art/android_debug_notification.gif b/.art/android_debug_notification.gif
deleted file mode 100644
index 358b636b..00000000
Binary files a/.art/android_debug_notification.gif and /dev/null differ
diff --git a/.art/ios_debug_notification_types.png b/.art/ios_debug_notification_types.png
deleted file mode 100644
index b417a2f9..00000000
Binary files a/.art/ios_debug_notification_types.png and /dev/null differ
diff --git a/.art/ios_debug_notifications.gif b/.art/ios_debug_notifications.gif
deleted file mode 100644
index a00427e4..00000000
Binary files a/.art/ios_debug_notifications.gif and /dev/null differ
diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
index 66b4ef08..47b562a7 100644
--- a/.github/workflows/examples.yml
+++ b/.github/workflows/examples.yml
@@ -11,9 +11,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v1
+ - uses: actions/setup-java@v4
with:
- java-version: 17
+ distribution: 'temurin'
+ java-version: '21'
- uses: subosito/flutter-action@v2
with:
channel: "stable"
diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml
index 04c38b48..fd343a59 100644
--- a/.github/workflows/no-response.yml
+++ b/.github/workflows/no-response.yml
@@ -13,6 +13,6 @@ jobs:
noResponse:
runs-on: ubuntu-latest
steps:
- - uses: lee-dohm/no-response@v0.5.0
+ - uses: bugwelle/no-response-action@main
with:
token: ${{ github.token }}
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 671dbed6..075fea26 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -12,7 +12,7 @@ jobs:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- - uses: amannn/action-semantic-pull-request@v5.2.0
+ - uses: amannn/action-semantic-pull-request@v5.4.0
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9063cdd6..483eb675 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -28,24 +28,29 @@ jobs:
with:
channel: 'stable'
- name: Build iOS App
- run: cd example && flutter build ios --debug --no-codesign
- - name: Run native iOS tests
- run: cd example/ios && xcodebuild -workspace Runner.xcworkspace -scheme Runner -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12,OS=16.2' test
+ run: |
+ dart pub global activate melos
+ melos bootstrap
+ cd example && flutter build ios --debug --no-codesign
+ cd ios && xcodebuild -workspace Runner.xcworkspace -scheme Runner -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12,OS=16.2' test
native_android_tests:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v1
+ - uses: actions/setup-java@v4
with:
- java-version: 17
+ distribution: 'temurin'
+ java-version: '21'
- uses: subosito/flutter-action@v1
with:
channel: 'stable'
- name: Build Android App
- run: cd example && flutter build apk --debug
- - name: Run native Android tests
- run: cd example/android && gradle workmanager:test
+ run: |
+ dart pub global activate melos
+ melos bootstrap
+ cd example && flutter build apk --debug
+ cd android && gradle workmanager:test
drive_ios:
strategy:
@@ -55,7 +60,7 @@ jobs:
fail-fast: false
runs-on: macos-latest
steps:
- - uses: futureware-tech/simulator-action@v1
+ - uses: futureware-tech/simulator-action@v3
with:
model: '${{ matrix.device }}'
- uses: actions/checkout@v4
@@ -64,7 +69,10 @@ jobs:
channel: 'stable'
# Run flutter integrate tests
- name: Run Flutter integration tests
- run: cd example && flutter test integration_test/workmanager_integration_test.dart
+ run: |
+ dart pub global activate melos
+ melos bootstrap
+ cd example && flutter test integration_test/workmanager_integration_test.dart
drive_android:
@@ -78,9 +86,10 @@ jobs:
target: [default]
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v1
+ - uses: actions/setup-java@v4
with:
- java-version: 17
+ distribution: 'temurin'
+ java-version: '21'
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
@@ -89,4 +98,7 @@ jobs:
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
- script: cd example && flutter test integration_test/workmanager_integration_test.dart
+ script: |
+ dart pub global activate melos
+ melos bootstrap
+ cd example && flutter test integration_test/workmanager_integration_test.dart
diff --git a/ANDROID_SETUP.md b/ANDROID_SETUP.md
index 6c519a66..553dba92 100644
--- a/ANDROID_SETUP.md
+++ b/ANDROID_SETUP.md
@@ -1,24 +1,6 @@
-# Check your AndroidManifest.xml
-
-Check if you have the following in your `AndroidManifest.xml` file.
-
-```xml
-
-```
-
-Ideally you should have this, if not follow the [upgrade guide](https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects).
-If for some reason you can't upgrade yet we still support the [older way of embedding](ANDROID_SETUP_V1.md):
# How to Debug my background job
Debugging a background task can be difficult, Android decides when is the best time to run.
There is no guaranteed way to enforce a run of a job even in debug mode.
-However to facilitate debugging, the plugin provides an `isInDebugMode` flag when initializing the plugin: `Workmanager().initialize(callbackDispatcher, isInDebugMode: true)`
-
-Once this flag is enabled you will receive a notification whenever a background task was triggered.
-This way you can keep track whether that task ran successfully or not.
-
-
diff --git a/IOS_SETUP.md b/IOS_SETUP.md
index a7525bb6..2055c0fe 100644
--- a/IOS_SETUP.md
+++ b/IOS_SETUP.md
@@ -177,70 +177,6 @@ If you launched your app using the Flutter command line tools or another IDE lik
-## Debug mode
-
-To make background work more visible when developing, the WorkManager plugin provides an `isInDebugMode` flag when initializing the plugin:
-
-```dart
-Workmanager().initialize(callbackDispatcher, isInDebugMode: true)
-```
-
-If `isInDebugMode` is `true`, a local notification will be displayed whenever a background fetch was triggered by iOS. In the example gif below, two background fetches were *simulated* in quick succession. Both completing succesfully after a few seconds in this case:
-
-
-
-
-
-These are the three notification types, **start** work, finished **successfully**, finished with **failure**:
-
-
-
-*Success* or *failure* depending on what you return in Dart:
-
-```dart
-bool success = true;
-return Future.value(success);
-```
-
-
-
-If your app is running in the **foreground**, notification banners are **not shown**. That is default behaviour on iOS. Triggering *Simulate Background Fetch* when running on a **real device** will put the app in the background *first*, before calling the `performFetchWithCompletionHandler`. This doesn't happen in the Simulator. So, make sure to go to the Home screen *before* triggering background fetch.
-
-> 📝 Note: the Home Indicator swipe-up gesture is sometimes tricky to do on a simulator. Use Simulator menu `Hardware` → `Home` or keyboard shortcut ⌘ command + ⇧ shift + H to make your life easier
-
-### Show notifications in foreground
-
-Alternatively, if you *do* want the banners to appear while your app is in the foreground you can implement `userNotificationCenter(_:willPresent:withCompletionHandler:)` of `UNUserNotificationCenterDelegate`.
-
-From the [Apple docs](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter):
-
-> If your **app is in the foreground** when a notification arrives, the shared user **notification center calls this method to deliver the notification directly to your app**. If you implement this method, you can take whatever actions are necessary to process the notification and update your app. When you finish, **call the completionHandler block and specify how you want the system to alert the user**, if at all.
-
-An easy way to get it working is by implementing it your `AppDelegate` like so:
-```swift
-class AppDelegate: FlutterAppDelegate {
- // ...
- override func userNotificationCenter(_ center: UNUserNotificationCenter,
- willPresent notification: UNNotification,
- withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
- completionHandler(.alert) // shows banner even if app is in foreground
- }
-
-}
-```
-
-And assigning yourself as the delegate. Preferably as early as possible, in `application:didFinishLaunchingWithOptions:`
-
-```swift
-UNUserNotificationCenter.current().delegate = self
-```
-
-Now the banners **are** shown when your app is running in the foreground 👇
-
-
-
-> 📝 Note: decide if implementing the delegate call makes sense for your app. You could make use of [active compilation conditions](https://blog.krzyzanowskim.com/2016/10/10/conditional-swift-testing/) to implement it *only* for the `Debug` configruation, for example
-
## Registered plugins
diff --git a/README.md b/README.md
index d600759b..50eb85b9 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Flutter Workmanager
[](https://pub.dartlang.org/packages/workmanager)
-[](https://cirrus-ci.com/github/vrtdev/flutter_workmanager/)
+
=======
Flutter WorkManager is a wrapper around [Android's WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager), [iOS' performFetchWithCompletionHandler](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623125-application) and [iOS BGAppRefreshTask](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask), effectively enabling headless execution of Dart code in the background.
@@ -10,338 +10,4 @@ For iOS users, please watch this video on a general introduction to background
This is especially useful to run periodic tasks, such as fetching remote data on a regular basis.
-> This plugin was featured in this [Medium blogpost](https://medium.com/vrt-digital-studio/flutter-workmanager-81e0cfbd6f6e)
-
-# Platform Setup
-
-In order for background work to be scheduled correctly you should follow the Android and iOS setup first.
-
-- [Android Setup](https://github.com/fluttercommunity/flutter_workmanager/blob/master/ANDROID_SETUP.md)
-- [iOS Setup](https://github.com/fluttercommunity/flutter_workmanager/blob/master/IOS_SETUP.md)
-
-# How to use the package?
-
-See sample folder for a complete working example.
-Before registering any task, the WorkManager plugin must be initialized.
-
-```dart
-@pragma('vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
-void callbackDispatcher() {
- Workmanager().executeTask((task, inputData) {
- print("Native called background task: $task"); //simpleTask will be emitted here.
- return Future.value(true);
- });
-}
-
-void main() {
- Workmanager().initialize(
- callbackDispatcher, // The top level function, aka callbackDispatcher
- isInDebugMode: true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
- );
- Workmanager().registerOneOffTask("task-identifier", "simpleTask");
- runApp(MyApp());
-}
-```
-
-> The `callbackDispatcher` needs to be either a static function or a top level function to be accessible as a Flutter entry point.
-
-The workmanager runs on a separate isolate from the main flutter isolate. Ensure to initialize all dependencies inside the `Workmanager().executeTask`.
-
-##### Debugging tips
-
-Wrap the code inside your `Workmanager().executeTask` in a `try and catch` in order to catch any exceptions thrown.
-
-```dart
-@pragma('vm:entry-point')
-void callbackDispatcher() {
- Workmanager().executeTask((task, inputData) async {
-
- int? totalExecutions;
- final _sharedPreference = await SharedPreferences.getInstance(); //Initialize dependency
-
- try { //add code execution
- totalExecutions = _sharedPreference.getInt("totalExecutions");
- _sharedPreference.setInt("totalExecutions", totalExecutions == null ? 1 : totalExecutions+1);
- } catch(err) {
- Logger().e(err.toString()); // Logger flutter package, prints error on the debug console
- throw Exception(err);
- }
-
- return Future.value(true);
- });
-}
-```
-
-Android tasks are identified using their `taskName`.
-iOS tasks are identified using their `taskIdentifier`.
-
-However, there is an exception for iOS background fetch: `Workmanager.iOSBackgroundTask`, a constant for iOS background fetch task.
-
----
-
-# Work Result
-
-The `Workmanager().executeTask(...` block supports 3 possible outcomes:
-
-1. `Future.value(true)`: The task is successful.
-2. `Future.value(false)`: The task did not complete successfully and needs to be retried. On Android, the retry is done automatically. On iOS (when using BGTaskScheduler), the retry needs to be scheduled manually.
-3. `Future.error(...)`: The task failed.
-
-On Android, the `BackoffPolicy` will configure how `WorkManager` is going to retry the task.
-
-Refer to the example app for a successful, retrying and a failed task.
-
-# iOS specific setup and note
-
-Initialize Workmanager only once.
-Background app refresh can only be tested on a real device, it cannot be tested on a simulator.
-
-### Migrate to 0.6.x
-Version 0.6.x of this plugin has some breaking changes for iOS:
-- Workmanager.registerOneOffTask was previously using iOS **BGProcessingTask**, now it will be an immediate run task which will continue in the background if user leaves the App. Since the previous solution meant the one off task will only run if the device is idle and as often experienced only when device is charging, in practice it means somewhere at night, or not at all during that day, because **BGProcessingTask** is meant for long running tasks. The new solution makes it more in line with Android except it does not support **initialDelay**
-- If you need the old behavior you can use the new iOS only method `Workmanager.registerProcessingTask`:
- 1. Replace `Workmanager().registerOneOffTask` with `Workmanager().registerProcessingTask` in your App
- 1. Replace `WorkmanagerPlugin.registerTask` with `WorkmanagerPlugin.registerBGProcessingTask` in `AppDelegate.swift`
-- Workmanager.registerOneOffTask does not support **initialDelay**
-- Workmanager.registerOneOffTask now supports **inputData** which was always returning null in the previous solution
-- Workmanager.registerOneOffTask now does NOT require `WorkmanagerPlugin.registerTask` call in `AppDelegate.swift` hence remove the call
-
-### One off tasks
-iOS supports **One off tasks** only on iOS 13+ with a few basic constraints:
-
-`registerOneOffTask` starts immediately. It might run for only 30 seconds due to iOS restrictions.
-
-```dart
-Workmanager().registerOneOffTask(
- "task-identifier",
- simpleTaskKey, // Ignored on iOS
- initialDelay: Duration(minutes: 30), // Ignored on iOS
- inputData: ... // fully supported
-);
-```
-
-### Periodic tasks
-iOS supports two types of **Periodic tasks**:
-- On iOS 12 and lower you can use deprecated Background Fetch API, see [iOS Setup](./IOS_SETUP.md), even though the API is
-deprecated by iOS it still works on iOS 13+ as of writing this article
-
-- `registerPeriodicTask` is only supported on iOS 13+, it might run for only 30 seconds due to iOS restrictions, but doesn't start immediately, rather iOS will schedule it as per user's App usage pattern.
-
-> ⚠️ On iOS 13+, adding a `BGTaskSchedulerPermittedIdentifiers` key to the Info.plist for new `BGTaskScheduler` API disables the `performFetchWithCompletionHandler` and `setMinimumBackgroundFetchInterval`
-methods, which means you cannot use both old Background Fetch and new `registerPeriodicTask` at the same time, you have to choose one based on your minimum iOS target version.
-For details see [Apple Docs](https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/using_background_tasks_to_update_your_app)
-
-To use `registerPeriodicTask` first register the task in `Info.plist` and `AppDelegate.swift` [iOS Setup](./IOS_SETUP.md). Unlike Android, for iOS you have to set the frequency in `AppDelegate.swift`. The frequency is not guaranteed rather iOS will schedule it as per user's App usage pattern, iOS might take a few days to learn usage pattern. In reality frequency just means do not repeat the task before x seconds/minutes. If frequency is not provided it will default to 15 minutes.
-
-```objc
-// Register a periodic task with 20 minutes frequency. The frequency is in seconds.
-WorkmanagerPlugin.registerPeriodicTask(withIdentifier: "be.tramckrijte.workmanagerExample.iOSBackgroundAppRefresh", frequency: NSNumber(value: 20 * 60))
-```
-
-Then schedule the task from your App
-```dart
-const iOSBackgroundAppRefresh = "be.tramckrijte.workmanagerExample.iOSBackgroundAppRefresh";
-Workmanager().registerPeriodicTask(
- iOSBackgroundAppRefresh,
- iOSBackgroundAppRefresh,
- initialDelay: Duration(seconds: 10),
- frequency: Duration(hours: 1), // Ignored on iOS, rather set in AppDelegate.swift
- inputData: ... // Not supported
-);
-```
-
-For more information see [BGAppRefreshTask](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask)
-
-### Processing tasks
-iOS supports **Processing tasks** only on iOS 13+ which can run for more than 30 seconds.
-
-`registerProcessingTask` is a long running one off background task, currently only for iOS. It can be run for more than 30 seconds but doesn't start immediately, rather iOS might schedule it when device is idle and charging.
-Processing tasks are for long processes like data processing and app maintenance. Processing tasks can run for minutes, but the system can interrupt these.
-iOS might terminate any running background processing tasks when the user starts using the device.
-For more information see [BGProcessingTask](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask)
-
-```dart
-const iOSBackgroundProcessingTask = "be.tramckrijte.workmanagerExample.iOSBackgroundProcessingTask";
-Workmanager().registerProcessingTask(
- iOSBackgroundProcessingTask,
- iOSBackgroundProcessingTask,
- initialDelay: Duration(minutes: 2),
- constraints: Constraints(
- // Connected or metered mark the task as requiring internet
- networkType: NetworkType.connected,
- // Require external power
- requiresCharging: true,
- ),
-);
-```
-
-### Background App Refresh permission
-
-On iOS user can disable `Background App Refresh` permission anytime, hence background tasks can only run if user has granted the permission.
-
-Use `permision_handler` to check for the permission:
-
-``` dart
-final status = await Permission.backgroundRefresh.status;
-if (status != PermissionStatus.granted) {
- _showNoPermission(context, status);
- return;
-}
-```
-
-For more information see the [BGTaskScheduler documentation](https://developer.apple.com/documentation/backgroundtasks).
-
-### Print scheduled tasks
-On iOS you can print scheduled tasks using `Workmanager.printScheduledTasks`
-
-It prints task details to console. To be used during development/debugging.
-Currently only supported on iOS and only on iOS 13+.
-
-```dart
-if (Platform.isIOS) {
- Workmanager().printScheduledTasks();
- // Prints: [BGTaskScheduler] Task Identifier: iOSBackgroundAppRefresh earliestBeginDate: 2023.10.10 PM 11:10:12
- // Or: [BGTaskScheduler] There are no scheduled tasks
-}
-```
-
-
-# Customisation (Android)
-
-Not every `Android WorkManager` feature is ported.
-
-Two kinds of background tasks can be registered :
-
-- **One off task** : runs only once
-- **Periodic tasks** : runs indefinitely on a regular basis
-
-```dart
-// One off task registration
-Workmanager().registerOneOffTask(
- "oneoff-task-identifier",
- "simpleTask"
-);
-
-// Periodic task registration
-Workmanager().registerPeriodicTask(
- "periodic-task-identifier",
- "simplePeriodicTask",
- // When no frequency is provided the default 15 minutes is set.
- // Minimum frequency is 15 min. Android will automatically change your frequency to 15 min if you have configured a lower frequency.
- frequency: Duration(hours: 1),
-)
-```
-
-Each task must have an **unique name**;
-This allows cancellation of a started task.
-The second parameter is the `String` that will be sent to your `callbackDispatcher` function, indicating the task's _type_.
-
-## Tagging
-
-You can set the optional `tag` property.
-Handy for cancellation by `tag`.
-This is different from the unique name in that you can group multiple tasks under one tag.
-
-```dart
-Workmanager().registerOneOffTask("1", "simpleTask", tag: "tag");
-```
-
-## Existing Work Policy
-
-Indicates the desired behaviour when the same task is scheduled more than once.
-The default is `KEEP`
-
-```dart
-Workmanager().registerOneOffTask("1", "simpleTask", existingWorkPolicy: ExistingWorkPolicy.append);
-```
-
-## Initial Delay
-
-Indicates how along a task should waitbefore its first run.
-
-```dart
-Workmanager().registerOneOffTask("1", "simpleTask", initialDelay: Duration(seconds: 10));
-```
-
-## Constraints
-
-> Constraints are mapped at best effort to each platform. Android's WorkManager supports most of the specific constraints, whereas iOS tasks are limited.
-
-- NetworkType
- Constrains the type of network required for your work to run. For example, Connected.
- The `NetworkType` lists various network conditions. `.connected` & `.metered` will be mapped to [`requiresNetworkConnectivity`](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtaskrequest/3142242-requiresnetworkconnectivity) on iOS.
-- RequiresBatteryNotLow (Android only)
- When set to true, your work will not run if the device is in low battery mode.
- **Enabling the battery saving mode on the android device prevents the job from running**
-- RequiresCharging
- When set to true, your work will only run when the device is charging.
-- RequiresDeviceIdle (Android only)
- When set to true, this requires the user’s device to be idle before the work will run. This can be useful for running batched operations that might otherwise have a - negative performance impact on other apps running actively on the user’s device.
-- RequiresStorageNotLow (Android only)
- When set to true, your work will not run if the user’s storage space on the device is too low.
-
-```dart
-Workmanager().registerOneOffTask(
- "1",
- "simpleTask",
- constraints: Constraints(
- networkType: NetworkType.connected,
- requiresBatteryNotLow: true,
- requiresCharging: true,
- requiresDeviceIdle: true,
- requiresStorageNotLow: true
- )
-);
-```
-
-### InputData
-
-Add some input data for your task. Valid value types are: `int`, `bool`, `double`, `String` and their `list`
-
-```dart
- Workmanager().registerOneOffTask(
- "1",
- "simpleTask",
- inputData: {
- 'int': 1,
- 'bool': true,
- 'double': 1.0,
- 'string': 'string',
- 'array': [1, 2, 3],
- },
-);
-```
-
-## BackoffPolicy
-
-Indicates the waiting strategy upon task failure.
-The default is `BackoffPolicy.exponential`.
-You can also specify the delay.
-
-```dart
-Workmanager().registerOneOffTask("1", "simpleTask", backoffPolicy: BackoffPolicy.exponential, backoffPolicyDelay: Duration(seconds: 10));
-```
-
-## Cancellation
-
-A task can be cancelled in different ways :
-
-### By Tag
-
-Cancels the task that was previously registered using this **Tag**, if any.
-
-```dart
-Workmanager().cancelByTag("tag");
-```
-
-### By Unique Name
-
-```dart
-Workmanager().cancelByUniqueName("");
-```
-
-### All
-
-```dart
-Workmanager().cancelAll();
-```
+> This plugin was featured in this [Medium blogpost](https://medium.com/vrt-digital-studio/flutter-workmanager-81e0cfbd6f6e)
\ No newline at end of file
diff --git a/example/ios/Podfile b/example/ios/Podfile
index d58567a0..7dde0e20 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -42,4 +42,10 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
+
+ installer.pods_project.targets.each do |target|
+ target.build_configurations.each do |config|
+ config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
+ end
+ end
end
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 4edc23e4..62fc1af4 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -38,11 +38,11 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
integration_test: 13825b8a9334a850581300559b8839134b124670
- path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
+ path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
- shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
+ shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
-PODFILE CHECKSUM: b63d507eb7cc768afa26646638aaf07f371f6370
+PODFILE CHECKSUM: f1f673b308b29c90487ecf3ff69323d61453a8a9
COCOAPODS: 1.14.3
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index 851fd5ef..de3d5013 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -226,13 +226,13 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
+ BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
- DevelopmentTeam = 6KRGLYTFWP;
LastSwiftMigration = 1110;
};
9EA9C43226E8F58700E77F3E = {
@@ -244,7 +244,7 @@
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
- compatibilityVersion = "Xcode 3.2";
+ compatibilityVersion = "Xcode 15.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -359,13 +359,12 @@
buildActionMask = 2147483647;
files = (
);
- inputPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
- "${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
- outputPaths = (
- "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -377,19 +376,12 @@
buildActionMask = 2147483647;
files = (
);
- inputPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
- "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework",
- "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework",
- "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework",
- "${BUILT_PRODUCTS_DIR}/workmanager/workmanager.framework",
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
- outputPaths = (
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/workmanager.framework",
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -502,6 +494,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -583,6 +576,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -640,6 +634,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -651,6 +646,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -665,7 +661,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = 6KRGLYTFWP;
+ DEVELOPMENT_TEAM = 7X4LHQK32Q;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift
index d3b40c1f..70fb9d34 100644
--- a/example/ios/Runner/AppDelegate.swift
+++ b/example/ios/Runner/AppDelegate.swift
@@ -11,7 +11,6 @@ import workmanager
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GeneratedPluginRegistrant.register(with: self)
- UNUserNotificationCenter.current().delegate = self
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
// Registry in this case is the FlutterEngine that is created in Workmanager's
@@ -33,12 +32,4 @@ import workmanager
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
-
- override func userNotificationCenter(
- _ center: UNUserNotificationCenter,
- willPresent notification: UNNotification,
- withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
- completionHandler(.alert) // shows banner even if app is in foreground
- }
-
}
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 80021afa..597a38a5 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -134,7 +134,6 @@ class _MyAppState extends State {
if (!workmanagerInitialized) {
Workmanager().initialize(
callbackDispatcher,
- isInDebugMode: true,
);
setState(() => workmanagerInitialized = true);
}
diff --git a/melos.yaml b/melos.yaml
index 902e5a03..8bb6fd18 100644
--- a/melos.yaml
+++ b/melos.yaml
@@ -5,6 +5,8 @@ packages:
scripts:
get: melos exec -- dart pub get
+ updgrade: melos exec -- dart pub upgrade
+
generate:dart:
run: melos exec -c 1 --depends-on="build_runner" --no-flutter -- "dart run build_runner build --delete-conflicting-outputs"
description: Build all generated files for Dart packages in this project.
diff --git a/workmanager/README.md b/workmanager/README.md
new file mode 100644
index 00000000..e54e3f5d
--- /dev/null
+++ b/workmanager/README.md
@@ -0,0 +1,336 @@
+# Flutter Workmanager
+
+Flutter WorkManager is a wrapper around [Android's WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager), [iOS' performFetchWithCompletionHandler](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623125-application) and [iOS BGAppRefreshTask](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask), effectively enabling headless execution of Dart code in the background.
+
+# Platform Setup
+
+In order for background work to be scheduled correctly you should follow the Android and iOS setup first.
+
+- [Android Setup](https://github.com/fluttercommunity/flutter_workmanager/blob/master/ANDROID_SETUP.md)
+- [iOS Setup](https://github.com/fluttercommunity/flutter_workmanager/blob/master/IOS_SETUP.md)
+
+# How to use the package?
+
+See sample folder for a complete working example.
+Before registering any task, the WorkManager plugin must be initialized.
+
+```dart
+@pragma('vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
+void callbackDispatcher() {
+ Workmanager().executeTask((task, inputData) {
+ print("Native called background task: $task"); //simpleTask will be emitted here.
+ return Future.value(true);
+ });
+}
+
+void main() {
+ Workmanager().initialize(
+ callbackDispatcher, // The top level function, aka callbackDispatcher
+ );
+ Workmanager().registerOneOffTask("task-identifier", "simpleTask");
+ runApp(MyApp());
+}
+```
+
+> The `callbackDispatcher` needs to be either a static function or a top level function to be accessible as a Flutter entry point.
+
+The workmanager runs on a separate isolate from the main flutter isolate. Ensure to initialize all dependencies inside the `Workmanager().executeTask`.
+
+##### Debugging tips
+
+Wrap the code inside your `Workmanager().executeTask` in a `try and catch` in order to catch any exceptions thrown.
+
+```dart
+@pragma('vm:entry-point')
+void callbackDispatcher() {
+ Workmanager().executeTask((task, inputData) async {
+
+ int? totalExecutions;
+ final _sharedPreference = await SharedPreferences.getInstance(); //Initialize dependency
+
+ try { //add code execution
+ totalExecutions = _sharedPreference.getInt("totalExecutions");
+ _sharedPreference.setInt("totalExecutions", totalExecutions == null ? 1 : totalExecutions+1);
+ } catch(err) {
+ Logger().e(err.toString()); // Logger flutter package, prints error on the debug console
+ throw Exception(err);
+ }
+
+ return Future.value(true);
+ });
+}
+```
+
+Android tasks are identified using their `taskName`.
+iOS tasks are identified using their `taskIdentifier`.
+
+However, there is an exception for iOS background fetch: `Workmanager.iOSBackgroundTask`, a constant for iOS background fetch task.
+
+---
+
+# Work Result
+
+The `Workmanager().executeTask(...` block supports 3 possible outcomes:
+
+1. `Future.value(true)`: The task is successful.
+2. `Future.value(false)`: The task did not complete successfully and needs to be retried. On Android, the retry is done automatically. On iOS (when using BGTaskScheduler), the retry needs to be scheduled manually.
+3. `Future.error(...)`: The task failed.
+
+On Android, the `BackoffPolicy` will configure how `WorkManager` is going to retry the task.
+
+Refer to the example app for a successful, retrying and a failed task.
+
+# iOS specific setup and note
+
+Initialize Workmanager only once.
+Background app refresh can only be tested on a real device, it cannot be tested on a simulator.
+
+### Migrate to 0.6.x
+Version 0.6.x of this plugin has some breaking changes for iOS:
+- Workmanager.registerOneOffTask was previously using iOS **BGProcessingTask**, now it will be an immediate run task which will continue in the background if user leaves the App. Since the previous solution meant the one off task will only run if the device is idle and as often experienced only when device is charging, in practice it means somewhere at night, or not at all during that day, because **BGProcessingTask** is meant for long running tasks. The new solution makes it more in line with Android except it does not support **initialDelay**
+- If you need the old behavior you can use the new iOS only method `Workmanager.registerProcessingTask`:
+ 1. Replace `Workmanager().registerOneOffTask` with `Workmanager().registerProcessingTask` in your App
+ 1. Replace `WorkmanagerPlugin.registerTask` with `WorkmanagerPlugin.registerBGProcessingTask` in `AppDelegate.swift`
+- Workmanager.registerOneOffTask does not support **initialDelay**
+- Workmanager.registerOneOffTask now supports **inputData** which was always returning null in the previous solution
+- Workmanager.registerOneOffTask now does NOT require `WorkmanagerPlugin.registerTask` call in `AppDelegate.swift` hence remove the call
+
+### One off tasks
+iOS supports **One off tasks** only on iOS 13+ with a few basic constraints:
+
+`registerOneOffTask` starts immediately. It might run for only 30 seconds due to iOS restrictions.
+
+```dart
+Workmanager().registerOneOffTask(
+ "task-identifier",
+ simpleTaskKey, // Ignored on iOS
+ initialDelay: Duration(minutes: 30), // Ignored on iOS
+ inputData: ... // fully supported
+);
+```
+
+### Periodic tasks
+iOS supports two types of **Periodic tasks**:
+- On iOS 12 and lower you can use deprecated Background Fetch API, see [iOS Setup](./IOS_SETUP.md), even though the API is
+deprecated by iOS it still works on iOS 13+ as of writing this article
+
+- `registerPeriodicTask` is only supported on iOS 13+, it might run for only 30 seconds due to iOS restrictions, but doesn't start immediately, rather iOS will schedule it as per user's App usage pattern.
+
+> ⚠️ On iOS 13+, adding a `BGTaskSchedulerPermittedIdentifiers` key to the Info.plist for new `BGTaskScheduler` API disables the `performFetchWithCompletionHandler` and `setMinimumBackgroundFetchInterval`
+methods, which means you cannot use both old Background Fetch and new `registerPeriodicTask` at the same time, you have to choose one based on your minimum iOS target version.
+For details see [Apple Docs](https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/using_background_tasks_to_update_your_app)
+
+To use `registerPeriodicTask` first register the task in `Info.plist` and `AppDelegate.swift` [iOS Setup](./IOS_SETUP.md). Unlike Android, for iOS you have to set the frequency in `AppDelegate.swift`. The frequency is not guaranteed rather iOS will schedule it as per user's App usage pattern, iOS might take a few days to learn usage pattern. In reality frequency just means do not repeat the task before x seconds/minutes. If frequency is not provided it will default to 15 minutes.
+
+```objc
+// Register a periodic task with 20 minutes frequency. The frequency is in seconds.
+WorkmanagerPlugin.registerPeriodicTask(withIdentifier: "be.tramckrijte.workmanagerExample.iOSBackgroundAppRefresh", frequency: NSNumber(value: 20 * 60))
+```
+
+Then schedule the task from your App
+```dart
+const iOSBackgroundAppRefresh = "be.tramckrijte.workmanagerExample.iOSBackgroundAppRefresh";
+Workmanager().registerPeriodicTask(
+ iOSBackgroundAppRefresh,
+ iOSBackgroundAppRefresh,
+ initialDelay: Duration(seconds: 10),
+ frequency: Duration(hours: 1), // Ignored on iOS, rather set in AppDelegate.swift
+ inputData: ... // Not supported
+);
+```
+
+For more information see [BGAppRefreshTask](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask)
+
+### Processing tasks
+iOS supports **Processing tasks** only on iOS 13+ which can run for more than 30 seconds.
+
+`registerProcessingTask` is a long running one off background task, currently only for iOS. It can be run for more than 30 seconds but doesn't start immediately, rather iOS might schedule it when device is idle and charging.
+Processing tasks are for long processes like data processing and app maintenance. Processing tasks can run for minutes, but the system can interrupt these.
+iOS might terminate any running background processing tasks when the user starts using the device.
+For more information see [BGProcessingTask](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask)
+
+```dart
+const iOSBackgroundProcessingTask = "be.tramckrijte.workmanagerExample.iOSBackgroundProcessingTask";
+Workmanager().registerProcessingTask(
+ iOSBackgroundProcessingTask,
+ iOSBackgroundProcessingTask,
+ initialDelay: Duration(minutes: 2),
+ constraints: Constraints(
+ // Connected or metered mark the task as requiring internet
+ networkType: NetworkType.connected,
+ // Require external power
+ requiresCharging: true,
+ ),
+);
+```
+
+### Background App Refresh permission
+
+On iOS user can disable `Background App Refresh` permission anytime, hence background tasks can only run if user has granted the permission.
+
+Use `permision_handler` to check for the permission:
+
+``` dart
+final status = await Permission.backgroundRefresh.status;
+if (status != PermissionStatus.granted) {
+ _showNoPermission(context, status);
+ return;
+}
+```
+
+For more information see the [BGTaskScheduler documentation](https://developer.apple.com/documentation/backgroundtasks).
+
+### Print scheduled tasks
+On iOS you can print scheduled tasks using `Workmanager.printScheduledTasks`
+
+It prints task details to console. To be used during development/debugging.
+Currently only supported on iOS and only on iOS 13+.
+
+```dart
+if (Platform.isIOS) {
+ Workmanager().printScheduledTasks();
+ // Prints: [BGTaskScheduler] Task Identifier: iOSBackgroundAppRefresh earliestBeginDate: 2023.10.10 PM 11:10:12
+ // Or: [BGTaskScheduler] There are no scheduled tasks
+}
+```
+
+
+# Customisation (Android)
+
+Not every `Android WorkManager` feature is ported.
+
+Two kinds of background tasks can be registered :
+
+- **One off task** : runs only once
+- **Periodic tasks** : runs indefinitely on a regular basis
+
+```dart
+// One off task registration
+Workmanager().registerOneOffTask(
+ "oneoff-task-identifier",
+ "simpleTask"
+);
+
+// Periodic task registration
+Workmanager().registerPeriodicTask(
+ "periodic-task-identifier",
+ "simplePeriodicTask",
+ // When no frequency is provided the default 15 minutes is set.
+ // Minimum frequency is 15 min. Android will automatically change your frequency to 15 min if you have configured a lower frequency.
+ frequency: Duration(hours: 1),
+)
+```
+
+Each task must have an **unique name**;
+This allows cancellation of a started task.
+The second parameter is the `String` that will be sent to your `callbackDispatcher` function, indicating the task's _type_.
+
+## Tagging
+
+You can set the optional `tag` property.
+Handy for cancellation by `tag`.
+This is different from the unique name in that you can group multiple tasks under one tag.
+
+```dart
+Workmanager().registerOneOffTask("1", "simpleTask", tag: "tag");
+```
+
+## Existing Work Policy
+
+Indicates the desired behaviour when the same task is scheduled more than once.
+The default is `KEEP`
+
+```dart
+Workmanager().registerOneOffTask("1", "simpleTask", existingWorkPolicy: ExistingWorkPolicy.append);
+```
+
+## Initial Delay
+
+Indicates how along a task should waitbefore its first run.
+
+```dart
+Workmanager().registerOneOffTask("1", "simpleTask", initialDelay: Duration(seconds: 10));
+```
+
+## Constraints
+
+> Constraints are mapped at best effort to each platform. Android's WorkManager supports most of the specific constraints, whereas iOS tasks are limited.
+
+- NetworkType
+ Constrains the type of network required for your work to run. For example, Connected.
+ The `NetworkType` lists various network conditions. `.connected` & `.metered` will be mapped to [`requiresNetworkConnectivity`](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtaskrequest/3142242-requiresnetworkconnectivity) on iOS.
+- RequiresBatteryNotLow (Android only)
+ When set to true, your work will not run if the device is in low battery mode.
+ **Enabling the battery saving mode on the android device prevents the job from running**
+- RequiresCharging
+ When set to true, your work will only run when the device is charging.
+- RequiresDeviceIdle (Android only)
+ When set to true, this requires the user’s device to be idle before the work will run. This can be useful for running batched operations that might otherwise have a - negative performance impact on other apps running actively on the user’s device.
+- RequiresStorageNotLow (Android only)
+ When set to true, your work will not run if the user’s storage space on the device is too low.
+
+```dart
+Workmanager().registerOneOffTask(
+ "1",
+ "simpleTask",
+ constraints: Constraints(
+ networkType: NetworkType.connected,
+ requiresBatteryNotLow: true,
+ requiresCharging: true,
+ requiresDeviceIdle: true,
+ requiresStorageNotLow: true
+ )
+);
+```
+
+### InputData
+
+Add some input data for your task. Valid value types are: `int`, `bool`, `double`, `String` and their `list`
+
+```dart
+ Workmanager().registerOneOffTask(
+ "1",
+ "simpleTask",
+ inputData: {
+ 'int': 1,
+ 'bool': true,
+ 'double': 1.0,
+ 'string': 'string',
+ 'array': [1, 2, 3],
+ },
+);
+```
+
+## BackoffPolicy
+
+Indicates the waiting strategy upon task failure.
+The default is `BackoffPolicy.exponential`.
+You can also specify the delay.
+
+```dart
+Workmanager().registerOneOffTask("1", "simpleTask", backoffPolicy: BackoffPolicy.exponential, backoffPolicyDelay: Duration(seconds: 10));
+```
+
+## Cancellation
+
+A task can be cancelled in different ways :
+
+### By Tag
+
+Cancels the task that was previously registered using this **Tag**, if any.
+
+```dart
+Workmanager().cancelByTag("tag");
+```
+
+### By Unique Name
+
+```dart
+Workmanager().cancelByUniqueName("");
+```
+
+### All
+
+```dart
+Workmanager().cancelAll();
+```
diff --git a/workmanager/android/src/main/AndroidManifest.xml b/workmanager/android/src/main/AndroidManifest.xml
index a3233951..cc947c56 100644
--- a/workmanager/android/src/main/AndroidManifest.xml
+++ b/workmanager/android/src/main/AndroidManifest.xml
@@ -1,3 +1 @@
-
-
-
+
diff --git a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/BackgroundWorker.kt b/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/BackgroundWorker.kt
index 08367891..2299678d 100644
--- a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/BackgroundWorker.kt
+++ b/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/BackgroundWorker.kt
@@ -32,7 +32,6 @@ class BackgroundWorker(
const val PAYLOAD_KEY = "be.tramckrijte.workmanager.INPUT_DATA"
const val DART_TASK_KEY = "be.tramckrijte.workmanager.DART_TASK"
- const val IS_IN_DEBUG_MODE_KEY = "be.tramckrijte.workmanager.IS_IN_DEBUG_MODE_KEY"
const val BACKGROUND_CHANNEL_NAME =
"be.tramckrijte.workmanager/background_channel_work_manager"
@@ -47,9 +46,6 @@ class BackgroundWorker(
private val dartTask
get() = workerParams.inputData.getString(DART_TASK_KEY)!!
- private val isInDebug
- get() = workerParams.inputData.getBoolean(IS_IN_DEBUG_MODE_KEY, false)
-
private val randomThreadIdentifier = Random().nextInt()
private var engine: FlutterEngine? = null
@@ -81,18 +77,6 @@ class BackgroundWorker(
val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
val dartBundlePath = flutterLoader.findAppBundlePath()
- if (isInDebug) {
- DebugHelper.postTaskStarting(
- applicationContext,
- randomThreadIdentifier,
- dartTask,
- payload,
- callbackHandle,
- callbackInfo,
- dartBundlePath,
- )
- }
-
engine?.let { engine ->
backgroundChannel = MethodChannel(engine.dartExecutor, BACKGROUND_CHANNEL_NAME)
backgroundChannel.setMethodCallHandler(this@BackgroundWorker)
@@ -117,17 +101,6 @@ class BackgroundWorker(
private fun stopEngine(result: Result?) {
val fetchDuration = System.currentTimeMillis() - startTime
- if (isInDebug) {
- DebugHelper.postTaskCompleteNotification(
- applicationContext,
- randomThreadIdentifier,
- dartTask,
- payload,
- fetchDuration,
- result ?: Result.failure(),
- )
- }
-
// No result indicates we were signalled to stop by WorkManager. The result is already
// STOPPED, so no need to resolve another one.
if (result != null) {
diff --git a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/DebugHelper.kt b/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/DebugHelper.kt
deleted file mode 100644
index 8b70c5aa..00000000
--- a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/DebugHelper.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-package dev.fluttercommunity.workmanager
-
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.content.Context
-import android.os.Build
-import androidx.core.app.NotificationCompat
-import androidx.work.ListenableWorker
-import io.flutter.view.FlutterCallbackInformation
-import java.text.DateFormat
-import java.util.Date
-import java.util.concurrent.TimeUnit.MILLISECONDS
-
-object ThumbnailGenerator {
- fun mapResultToEmoji(result: ListenableWorker.Result): String =
- when (result) {
- is ListenableWorker.Result.Success -> "\uD83C\uDF89"
- else -> "\uD83D\uDD25"
- }
-
- val workEmoji get() = listOf("\uD83D\uDC77\u200D♀️", "\uD83D\uDC77\u200D♂️").random()
-}
-
-object DebugHelper {
- private const val DEBUG_CHANNEL_ID = "WorkmanagerDebugChannelId"
- private const val DEBUG_CHANNEL_NAME = "A helper channel to debug your background tasks."
- private val debugDateFormatter =
- DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
-
- private val currentTime get() = debugDateFormatter.format(Date())
-
- private fun mapMillisToSeconds(milliseconds: Long) = "${MILLISECONDS.toSeconds(milliseconds)} seconds."
-
- fun postTaskCompleteNotification(
- ctx: Context,
- threadIdentifier: Int,
- dartTask: String,
- payload: String? = null,
- fetchDuration: Long,
- result: ListenableWorker.Result,
- ) {
- postNotification(
- ctx,
- threadIdentifier,
- "${ThumbnailGenerator.workEmoji} $currentTime",
- """
- • Result: ${ThumbnailGenerator.mapResultToEmoji(result)} ${result.javaClass.simpleName}
- • dartTask: $dartTask
- • inputData: ${payload ?: "not found"}
- • Elapsed time: ${mapMillisToSeconds(fetchDuration)}
- """.trimIndent(),
- )
- }
-
- fun postTaskStarting(
- ctx: Context,
- threadIdentifier: Int,
- dartTask: String,
- payload: String? = null,
- callbackHandle: Long,
- callbackInfo: FlutterCallbackInformation?,
- dartBundlePath: String?,
- ) {
- postNotification(
- ctx,
- threadIdentifier,
- "${ThumbnailGenerator.workEmoji} $currentTime",
- """
- • dartTask: $dartTask
- • inputData: ${payload ?: "not found"}
- • callbackHandle: $callbackHandle
- • callBackName: ${callbackInfo?.callbackName ?: "not found"}
- • callbackClassName: ${callbackInfo?.callbackClassName ?: "not found"}
- • callbackLibraryPath: ${callbackInfo?.callbackLibraryPath ?: "not found"}
- • dartBundlePath: $dartBundlePath"
- """.trimIndent(),
- )
- }
-
- private fun postNotification(
- ctx: Context,
- messageId: Int,
- title: String,
- contentText: String,
- ) {
- (ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).apply {
- createNotificationChannel()
-
- notify(
- messageId,
- NotificationCompat.Builder(ctx, DEBUG_CHANNEL_ID)
- .setContentTitle(title)
- .setContentText(contentText)
- .setStyle(
- NotificationCompat.BigTextStyle()
- .bigText(contentText),
- )
- .setSmallIcon(android.R.drawable.stat_notify_sync)
- .build(),
- )
- }
- }
-
- private fun NotificationManager.createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createNotificationChannel(
- NotificationChannel(
- DEBUG_CHANNEL_ID,
- DEBUG_CHANNEL_NAME,
- NotificationManager.IMPORTANCE_DEFAULT,
- ),
- )
- }
- }
-}
diff --git a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/Extractor.kt b/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/Extractor.kt
index 4ac69596..8a7d960c 100644
--- a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/Extractor.kt
+++ b/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/Extractor.kt
@@ -13,7 +13,6 @@ import androidx.work.WorkRequest
import dev.fluttercommunity.workmanager.WorkManagerCall.CancelTask.ByTag.KEYS.UNREGISTER_TASK_TAG_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.CancelTask.ByUniqueName.KEYS.UNREGISTER_TASK_UNIQUE_NAME_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.Initialize.KEYS.INITIALIZE_TASK_CALL_HANDLE_KEY
-import dev.fluttercommunity.workmanager.WorkManagerCall.Initialize.KEYS.INITIALIZE_TASK_IS_IN_DEBUG_MODE_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.IsScheduled.ByUniqueName.KEYS.IS_SCHEDULED_UNIQUE_NAME_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_BACK_OFF_POLICY_DELAY_MILLIS_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_BACK_OFF_POLICY_TYPE_KEY
@@ -24,7 +23,6 @@ import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGIST
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_CONSTRAINTS_STORAGE_NOT_LOW_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_EXISTING_WORK_POLICY_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_INITIAL_DELAY_SECONDS_KEY
-import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_IS_IN_DEBUG_MODE_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_NAME_VALUE_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_OUT_OF_QUOTA_POLICY_KEY
import dev.fluttercommunity.workmanager.WorkManagerCall.RegisterTask.KEYS.REGISTER_TASK_PAYLOAD_KEY
@@ -59,16 +57,13 @@ data class BackoffPolicyTaskConfig(
sealed class WorkManagerCall {
data class Initialize(
val callbackDispatcherHandleKey: Long,
- val isInDebugMode: Boolean,
) : WorkManagerCall() {
companion object KEYS {
- const val INITIALIZE_TASK_IS_IN_DEBUG_MODE_KEY = "isInDebugMode"
const val INITIALIZE_TASK_CALL_HANDLE_KEY = "callbackHandle"
}
}
sealed class RegisterTask : WorkManagerCall() {
- abstract val isInDebugMode: Boolean
abstract val uniqueName: String
abstract val taskName: String
abstract val tag: String?
@@ -77,7 +72,6 @@ sealed class WorkManagerCall {
abstract val payload: String?
companion object KEYS {
- const val REGISTER_TASK_IS_IN_DEBUG_MODE_KEY = "isInDebugMode"
const val REGISTER_TASK_UNIQUE_NAME_KEY = "uniqueName"
const val REGISTER_TASK_NAME_VALUE_KEY = "taskName"
const val REGISTER_TASK_TAG_KEY = "tag"
@@ -98,7 +92,6 @@ sealed class WorkManagerCall {
}
data class OneOffTask(
- override val isInDebugMode: Boolean,
override val uniqueName: String,
override val taskName: String,
override val tag: String? = null,
@@ -111,7 +104,6 @@ sealed class WorkManagerCall {
) : RegisterTask()
data class PeriodicTask(
- override val isInDebugMode: Boolean,
override val uniqueName: String,
override val taskName: String,
override val tag: String? = null,
@@ -194,17 +186,15 @@ object Extractor {
when (PossibleWorkManagerCall.fromRawMethodName(call.method)) {
PossibleWorkManagerCall.INITIALIZE -> {
val handle = call.argument(INITIALIZE_TASK_CALL_HANDLE_KEY)?.toLong()
- val inDebugMode = call.argument(INITIALIZE_TASK_IS_IN_DEBUG_MODE_KEY)
- if (handle == null || inDebugMode == null) {
+ if (handle == null) {
WorkManagerCall.Failed("Invalid parameters passed")
} else {
- WorkManagerCall.Initialize(handle, inDebugMode)
+ WorkManagerCall.Initialize(handle)
}
}
PossibleWorkManagerCall.REGISTER_ONE_OFF_TASK -> {
WorkManagerCall.RegisterTask.OneOffTask(
- isInDebugMode = call.argument(REGISTER_TASK_IS_IN_DEBUG_MODE_KEY)!!,
uniqueName = call.argument(REGISTER_TASK_UNIQUE_NAME_KEY)!!,
taskName = call.argument(REGISTER_TASK_NAME_VALUE_KEY)!!,
tag = call.argument(REGISTER_TASK_TAG_KEY),
@@ -222,7 +212,6 @@ object Extractor {
}
PossibleWorkManagerCall.REGISTER_PERIODIC_TASK -> {
WorkManagerCall.RegisterTask.PeriodicTask(
- isInDebugMode = call.argument(REGISTER_TASK_IS_IN_DEBUG_MODE_KEY)!!,
uniqueName = call.argument(REGISTER_TASK_UNIQUE_NAME_KEY)!!,
taskName = call.argument(REGISTER_TASK_NAME_VALUE_KEY)!!,
frequencyInSeconds = extractFrequencySecondsFromCall(call),
diff --git a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/WorkmanagerCallHandler.kt b/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/WorkmanagerCallHandler.kt
index 3b12de86..cc4b5f4a 100644
--- a/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/WorkmanagerCallHandler.kt
+++ b/workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/WorkmanagerCallHandler.kt
@@ -11,7 +11,6 @@ import androidx.work.PeriodicWorkRequest
import androidx.work.WorkInfo
import androidx.work.WorkManager
import dev.fluttercommunity.workmanager.BackgroundWorker.Companion.DART_TASK_KEY
-import dev.fluttercommunity.workmanager.BackgroundWorker.Companion.IS_IN_DEBUG_MODE_KEY
import dev.fluttercommunity.workmanager.BackgroundWorker.Companion.PAYLOAD_KEY
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
@@ -41,30 +40,35 @@ class WorkmanagerCallHandler(private val ctx: Context) : MethodChannel.MethodCal
extractedCall,
result,
)
+
is WorkManagerCall.RegisterTask ->
RegisterTaskHandler.handle(
ctx,
extractedCall,
result,
)
+
is WorkManagerCall.IsScheduled ->
IsScheduledHandler.handle(
ctx,
extractedCall,
result,
)
+
is WorkManagerCall.CancelTask ->
UnregisterTaskHandler.handle(
ctx,
extractedCall,
result,
)
+
is WorkManagerCall.Failed ->
FailedTaskHandler(extractedCall.code).handle(
ctx,
extractedCall,
result,
)
+
is WorkManagerCall.Unknown -> UnknownTaskHandler.handle(ctx, extractedCall, result)
}
}
@@ -130,7 +134,6 @@ private object RegisterTaskHandler : CallHandler {
tag = convertedCall.tag,
flexIntervalInSeconds = convertedCall.flexIntervalInSeconds,
frequencyInSeconds = convertedCall.frequencyInSeconds,
- isInDebugMode = convertedCall.isInDebugMode,
existingWorkPolicy = convertedCall.existingWorkPolicy,
initialDelaySeconds = convertedCall.initialDelaySeconds,
constraintsConfig = convertedCall.constraintsConfig,
@@ -149,7 +152,6 @@ private object RegisterTaskHandler : CallHandler {
uniqueName = convertedCall.uniqueName,
dartTask = convertedCall.taskName,
tag = convertedCall.tag,
- isInDebugMode = convertedCall.isInDebugMode,
existingWorkPolicy = convertedCall.existingWorkPolicy,
initialDelaySeconds = convertedCall.initialDelaySeconds,
constraintsConfig = convertedCall.constraintsConfig,
@@ -190,6 +192,7 @@ private object UnregisterTaskHandler : CallHandler {
context,
convertedCall.uniqueName,
)
+
is WorkManagerCall.CancelTask.ByTag -> WM.cancelByTag(context, convertedCall.tag)
WorkManagerCall.CancelTask.All -> WM.cancelAll(context)
}
@@ -224,7 +227,6 @@ object WM {
dartTask: String,
payload: String? = null,
tag: String? = null,
- isInDebugMode: Boolean = false,
existingWorkPolicy: ExistingWorkPolicy = defaultOneOffExistingWorkPolicy,
initialDelaySeconds: Long = DEFAULT_INITIAL_DELAY_SECONDS,
constraintsConfig: Constraints = defaultConstraints,
@@ -233,7 +235,7 @@ object WM {
) {
val oneOffTaskRequest =
OneTimeWorkRequest.Builder(BackgroundWorker::class.java)
- .setInputData(buildTaskInputData(dartTask, isInDebugMode, payload))
+ .setInputData(buildTaskInputData(dartTask, payload))
.setInitialDelay(initialDelaySeconds, TimeUnit.SECONDS)
.setConstraints(constraintsConfig)
.apply {
@@ -262,7 +264,6 @@ object WM {
tag: String? = null,
frequencyInSeconds: Long = DEFAULT_PERIODIC_REFRESH_FREQUENCY_SECONDS,
flexIntervalInSeconds: Long = DEFAULT_FLEX_INTERVAL_SECONDS,
- isInDebugMode: Boolean = false,
existingWorkPolicy: ExistingPeriodicWorkPolicy = defaultPeriodExistingWorkPolicy,
initialDelaySeconds: Long = DEFAULT_INITIAL_DELAY_SECONDS,
constraintsConfig: Constraints = defaultConstraints,
@@ -277,7 +278,7 @@ object WM {
flexIntervalInSeconds,
TimeUnit.SECONDS,
)
- .setInputData(buildTaskInputData(dartTask, isInDebugMode, payload))
+ .setInputData(buildTaskInputData(dartTask, payload))
.setInitialDelay(initialDelaySeconds, TimeUnit.SECONDS)
.setConstraints(constraintsConfig)
.apply {
@@ -300,12 +301,10 @@ object WM {
private fun buildTaskInputData(
dartTask: String,
- isInDebugMode: Boolean,
payload: String?,
): Data {
return Data.Builder()
.putString(DART_TASK_KEY, dartTask)
- .putBoolean(IS_IN_DEBUG_MODE_KEY, isInDebugMode)
.apply {
payload?.let {
putString(PAYLOAD_KEY, payload)
diff --git a/workmanager/ios/Classes/BackgroundWorker.swift b/workmanager/ios/Classes/BackgroundWorker.swift
index 843f4c4d..8e210fe6 100644
--- a/workmanager/ios/Classes/BackgroundWorker.swift
+++ b/workmanager/ios/Classes/BackgroundWorker.swift
@@ -72,13 +72,6 @@ class BackgroundWorker {
let taskSessionStart = Date()
let taskSessionIdentifier = UUID()
- let debugHelper = DebugNotificationHelper(taskSessionIdentifier)
- debugHelper.showStartFetchNotification(
- startDate: taskSessionStart,
- callBackHandle: callbackHandle,
- callbackInfo: flutterCallbackInformation
- )
-
var flutterEngine: FlutterEngine? = FlutterEngine(
name: backgroundMode.flutterThreadlabelPrefix,
project: nil,
@@ -121,11 +114,6 @@ class BackgroundWorker {
let taskDuration = taskSessionCompleter.timeIntervalSince(taskSessionStart)
logInfo("[\(String(describing: self))] \(#function) -> performBackgroundRequest.\(result) (finished in \(taskDuration.formatToSeconds()))")
- debugHelper.showCompletedFetchNotification(
- completedDate: taskSessionCompleter,
- result: result,
- elapsedTime: taskDuration
- )
completionHandler(result)
})
default:
diff --git a/workmanager/ios/Classes/DebugNotificationHelper.swift b/workmanager/ios/Classes/DebugNotificationHelper.swift
deleted file mode 100644
index 66ea0a9a..00000000
--- a/workmanager/ios/Classes/DebugNotificationHelper.swift
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-// LocalNotificationHelper.swift
-// workmanager
-//
-// Created by Kymer Gryson on 12/08/2019.
-//
-
-import Foundation
-import UserNotifications
-
-class DebugNotificationHelper {
-
- private let identifier: UUID
-
- init(_ identifier: UUID) {
- self.identifier = identifier
- }
-
- func showStartFetchNotification(startDate: Date,
- callBackHandle: Int64,
- callbackInfo: FlutterCallbackInformation
- ) {
- let message =
- """
- Starting Dart/Flutter with following params:
- • callbackHandle: '\(callBackHandle)'
- • callBackName: '\(callbackInfo.callbackName ?? "not found")'
- • callbackClassName: '\(callbackInfo.callbackClassName ?? "not found")'
- • callbackLibraryPath: '\(callbackInfo.callbackLibraryPath ?? "not found")'
- """
- DebugNotificationHelper.scheduleNotification(identifier: identifier.uuidString,
- title: startDate.formatted(),
- body: message,
- icon: .startWork)
- }
-
- func showCompletedFetchNotification(completedDate: Date,
- result: UIBackgroundFetchResult,
- elapsedTime: TimeInterval) {
- let message =
- """
- Perform fetch completed:
- • Elapsed time: \(elapsedTime.formatToSeconds())
- • Result: UIBackgroundFetchResult.\(result)
- """
- DebugNotificationHelper.scheduleNotification(identifier: identifier.uuidString,
- title: completedDate.formatted(),
- body: message,
- icon: result == .newData ? .success : .failure)
- }
-
- // MARK: - Private helper functions
-
- private static func scheduleNotification(identifier: String,
- title: String,
- body: String,
- icon: ThumbnailGenerator.ThumbnailIcon) {
- guard UserDefaultsHelper.getIsDebug() else {
- logInfo("\(logPrefix) \(#function): plugin is not running in debug mode or on iOS 9 or lower")
- return
- }
-
- UNUserNotificationCenter.current().requestAuthorization(options: [.sound, .alert]) { (_, _) in }
- let notificationRequest = createNotificationRequest(
- identifier: identifier,
- threadIdentifier: SwiftWorkmanagerPlugin.identifier,
- title: title,
- body: body,
- icon: icon
- )
- UNUserNotificationCenter.current().add(notificationRequest, withCompletionHandler: nil)
-
- }
-
- private static func createNotificationRequest(identifier: String,
- threadIdentifier: String,
- title: String,
- body: String,
- icon: ThumbnailGenerator.ThumbnailIcon) -> UNNotificationRequest {
- let notification = UNMutableNotificationContent()
- notification.title = title
- notification.body = body
- notification.threadIdentifier = threadIdentifier
- if let thumbnail = ThumbnailGenerator.createThumbnail(with: icon) {
- notification.attachments = [thumbnail]
- }
- let immediateFutureTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
- let notificationRequest = UNNotificationRequest(
- identifier: identifier,
- content: notification,
- trigger: immediateFutureTrigger
- )
-
- return notificationRequest
- }
-
- private static var logPrefix: String {
- return "\(String(describing: SwiftWorkmanagerPlugin.self)) - \(DebugNotificationHelper.self)"
- }
-
-}
diff --git a/workmanager/ios/Classes/SwiftWorkmanagerPlugin.swift b/workmanager/ios/Classes/SwiftWorkmanagerPlugin.swift
index c09d6ac0..b3bbbead 100644
--- a/workmanager/ios/Classes/SwiftWorkmanagerPlugin.swift
+++ b/workmanager/ios/Classes/SwiftWorkmanagerPlugin.swift
@@ -21,7 +21,6 @@ public class SwiftWorkmanagerPlugin: FlutterPluginAppLifeCycleDelegate {
struct Initialize {
static let name = "\(Initialize.self)".lowercasingFirst
enum Arguments: String {
- case isInDebugMode
case callbackHandle
}
}
@@ -293,13 +292,11 @@ extension SwiftWorkmanagerPlugin: FlutterPlugin {
private func initialize(arguments: [AnyHashable: Any], result: @escaping FlutterResult) {
let method = ForegroundMethodChannel.Methods.Initialize.self
- guard let isInDebug = arguments[method.Arguments.isInDebugMode.rawValue] as? Bool,
- let handle = arguments[method.Arguments.callbackHandle.rawValue] as? Int64 else {
+ guard let handle = arguments[method.Arguments.callbackHandle.rawValue] as? Int64 else {
result(WMPError.invalidParameters.asFlutterError)
return
}
UserDefaultsHelper.storeCallbackHandle(handle)
- UserDefaultsHelper.storeIsDebug(isInDebug)
result(true)
}
diff --git a/workmanager/ios/Classes/ThumbnailGenerator.swift b/workmanager/ios/Classes/ThumbnailGenerator.swift
deleted file mode 100644
index a6c64a40..00000000
--- a/workmanager/ios/Classes/ThumbnailGenerator.swift
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// ThumbnailGenerator.swift
-// workmanager
-//
-// Created by Kymer Gryson on 13/08/2019.
-//
-
-import Foundation
-import UserNotifications
-import UIKit
-
-struct ThumbnailGenerator {
-
- enum ThumbnailIcon {
- case startWork
- case success
- case failure
-
- var textValue: String {
- switch self {
- case .startWork:
- return ["👷♀️", "👷♂️"].randomElement()!
- case .success:
- return "🎉"
- case .failure:
- return "🔥"
- }
- }
- }
-
- static func createThumbnail(with icon: ThumbnailIcon) -> UNNotificationAttachment? {
- let name = "thumbnail"
- let thumbnailFrame = CGRect(x: 0, y: 0, width: 150, height: 150)
- let thumbnail = UIView(frame: thumbnailFrame)
- thumbnail.isOpaque = false
- let label = UILabel(frame: thumbnailFrame)
- label.text = icon.textValue
- label.font = UIFont.systemFont(ofSize: 125)
- label.textAlignment = .center
- thumbnail.addSubview(label)
-
- do {
- let thumbnailImage = try thumbnail.renderAsImage()
- let localURL = try thumbnailImage.persist(fileName: name)
- return try UNNotificationAttachment(
- identifier: "\(SwiftWorkmanagerPlugin.identifier).\(name)",
- url: localURL,
- options: nil
- )
- } catch {
- logInfo("\(logPrefix) \(#function) something went wrong creating a thumbnail for local debug notification")
- return nil
- }
-
- }
-
- private static var logPrefix: String {
- return "\(String(describing: SwiftWorkmanagerPlugin.self)) - \(ThumbnailGenerator.self)"
- }
-
-}
-
-private extension UIView {
-
- func renderAsImage() throws -> UIImage {
- UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
- defer { UIGraphicsEndImageContext() }
-
- guard let context = UIGraphicsGetCurrentContext() else {
- throw GraphicsError.noCurrentGraphicsContextFound
- }
- self.layer.render(in: context)
- guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
- throw GraphicsError.noCurrentGraphicsContextFound
- }
-
- return image
- }
-
- enum GraphicsError: Error {
- case noCurrentGraphicsContextFound
- }
-}
-
-private extension UIImage {
-
- func persist(fileName: String, in directory: URL = URL(fileURLWithPath: NSTemporaryDirectory())) throws -> URL {
- let directoryURL = directory.appendingPathComponent(SwiftWorkmanagerPlugin.identifier, isDirectory: true)
- let fileURL = directoryURL.appendingPathComponent("\(fileName).png")
- try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
- guard let imageData = self.pngData() else {
- throw ImageError.cannotRepresentAsPNG(self)
- }
- try imageData.write(to: fileURL)
-
- return fileURL
- }
-
- enum ImageError: Error {
- case cannotRepresentAsPNG(UIImage)
- }
-
-}
diff --git a/workmanager/ios/Classes/UserDefaultsHelper.swift b/workmanager/ios/Classes/UserDefaultsHelper.swift
index 562b0930..c3fdb280 100644
--- a/workmanager/ios/Classes/UserDefaultsHelper.swift
+++ b/workmanager/ios/Classes/UserDefaultsHelper.swift
@@ -15,7 +15,6 @@ struct UserDefaultsHelper {
enum Key {
case callbackHandle
- case isDebug
var stringValue: String {
return "\(SwiftWorkmanagerPlugin.identifier).\(self)"
@@ -32,16 +31,6 @@ struct UserDefaultsHelper {
return getValue(for: .callbackHandle)
}
- // MARK: isDebug
-
- static func storeIsDebug(_ isDebug: Bool) {
- store(isDebug, key: .isDebug)
- }
-
- static func getIsDebug() -> Bool {
- return getValue(for: .isDebug) ?? false
- }
-
// MARK: Private helper functions
private static func store(_ value: T, key: Key) {
diff --git a/workmanager/lib/src/workmanager.dart b/workmanager/lib/src/workmanager.dart
index e52c0327..5663cf95 100644
--- a/workmanager/lib/src/workmanager.dart
+++ b/workmanager/lib/src/workmanager.dart
@@ -124,8 +124,6 @@ class Workmanager {
static const String iOSBackgroundProcessingTask =
"workmanager.background.task";
- static bool _isInDebugMode = false;
-
MethodChannel _backgroundChannel = const MethodChannel(
"be.tramckrijte.workmanager/background_channel_work_manager");
MethodChannel _foregroundChannel = const MethodChannel(
@@ -149,12 +147,7 @@ class Workmanager {
/// This call is required if you wish to use the [WorkManager] plugin.
/// [callbackDispatcher] is a top level function which will be invoked by
/// Android or iOS. See the discussion on [BackgroundTaskHandler] for details.
- /// [isInDebugMode] true will post debug notifications with information about when a task should have run
- Future initialize(
- final Function callbackDispatcher, {
- final bool isInDebugMode = false,
- }) async {
- Workmanager._isInDebugMode = isInDebugMode;
+ Future initialize(final Function callbackDispatcher) async {
final callback = PluginUtilities.getCallbackHandle(callbackDispatcher);
assert(callback != null,
"The callbackDispatcher needs to be either a static function or a top level function to be accessible as a Flutter entry point.");
@@ -163,7 +156,6 @@ class Workmanager {
await _foregroundChannel.invokeMethod(
'initialize',
JsonMapperHelper.toInitializeMethodArgument(
- isInDebugMode: _isInDebugMode,
callbackHandle: handle,
),
);
@@ -207,7 +199,6 @@ class Workmanager {
await _foregroundChannel.invokeMethod(
"registerOneOffTask",
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: _isInDebugMode,
uniqueName: uniqueName,
taskName: taskName,
tag: tag,
@@ -259,7 +250,6 @@ class Workmanager {
await _foregroundChannel.invokeMethod(
"registerPeriodicTask",
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: _isInDebugMode,
uniqueName: uniqueName,
taskName: taskName,
frequency: frequency,
@@ -311,7 +301,6 @@ class Workmanager {
await _foregroundChannel.invokeMethod(
"registerProcessingTask",
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: _isInDebugMode,
uniqueName: uniqueName,
taskName: taskName,
initialDelay: initialDelay,
@@ -349,7 +338,6 @@ class Workmanager {
class JsonMapperHelper {
@visibleForTesting
static Map toRegisterMethodArgument({
- final bool isInDebugMode = false,
final String? uniqueName,
final String? taskName,
final Duration? frequency,
@@ -384,7 +372,6 @@ class JsonMapperHelper {
assert(uniqueName != null);
assert(taskName != null);
return {
- "isInDebugMode": isInDebugMode,
"uniqueName": uniqueName,
"taskName": taskName,
"tag": tag,
@@ -406,11 +393,9 @@ class JsonMapperHelper {
@visibleForTesting
static Map toInitializeMethodArgument({
- required final bool isInDebugMode,
required final int callbackHandle,
}) {
return {
- "isInDebugMode": isInDebugMode,
"callbackHandle": callbackHandle,
};
}
diff --git a/workmanager/test/json_mapper_helper_test.dart b/workmanager/test/json_mapper_helper_test.dart
index 44742e0a..405ba387 100644
--- a/workmanager/test/json_mapper_helper_test.dart
+++ b/workmanager/test/json_mapper_helper_test.dart
@@ -7,10 +7,8 @@ import 'package:workmanager/src/workmanager.dart';
void main() {
group("toInitializeMethodArgument", () {
test("all arguments given", () {
- expect(
- JsonMapperHelper.toInitializeMethodArgument(
- isInDebugMode: true, callbackHandle: 9001),
- {'isInDebugMode': true, 'callbackHandle': 9001});
+ expect(JsonMapperHelper.toInitializeMethodArgument(callbackHandle: 9001),
+ {'callbackHandle': 9001});
});
});
@@ -18,7 +16,7 @@ void main() {
group("invalid inputs", () {
test("no unique name", () {
expect(
- () => JsonMapperHelper.toRegisterMethodArgument(isInDebugMode: true),
+ () => JsonMapperHelper.toRegisterMethodArgument(),
throwsA(
const TypeMatcher(),
),
@@ -28,7 +26,7 @@ void main() {
test("no value", () {
expect(
() => JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true, uniqueName: "uniqueName"),
+ uniqueName: "uniqueName"),
throwsA(
const TypeMatcher(),
),
@@ -38,13 +36,11 @@ void main() {
test("initial delay was null", () {
expect(
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true,
uniqueName: "uniqueName",
taskName: "taskName",
initialDelay: null,
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -68,14 +64,12 @@ void main() {
test("backoff policy delay was null", () {
expect(
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true,
uniqueName: "uniqueName",
taskName: "taskName",
initialDelay: Duration(seconds: 1),
backoffPolicyDelay: null,
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -101,14 +95,12 @@ void main() {
test("minimum required fields", () {
expect(
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true,
uniqueName: "uniqueName",
taskName: "taskName",
initialDelay: Duration(seconds: 1),
backoffPolicyDelay: Duration(seconds: 1),
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -131,7 +123,6 @@ void main() {
test("All fields filled in", () {
expect(
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true,
uniqueName: "uniqueName",
taskName: "taskName",
frequency: Duration(seconds: 1),
@@ -151,7 +142,6 @@ void main() {
inputData: {"key": "value"},
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': 'tag',
@@ -183,7 +173,6 @@ void main() {
requiresBatteryNotLow: true,
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -211,7 +200,6 @@ void main() {
requiresBatteryNotLow: false,
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -233,7 +221,6 @@ void main() {
[
Constraints(networkType: NetworkType.metered),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -261,7 +248,6 @@ void main() {
requiresBatteryNotLow: true,
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -289,7 +275,6 @@ void main() {
requiresBatteryNotLow: true,
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -312,7 +297,6 @@ void main() {
test("map to JSON", () {
expect(
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true,
uniqueName: "uniqueName",
taskName: "taskName",
initialDelay: Duration(seconds: 1),
@@ -335,7 +319,6 @@ void main() {
test("for workpolicy ${existingWorkPolicy.first}", () {
expect(
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true,
uniqueName: "uniqueName",
taskName: "taskName",
initialDelay: Duration(seconds: 1),
@@ -343,7 +326,6 @@ void main() {
existingWorkPolicy:
existingWorkPolicy.first as ExistingWorkPolicy),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,
@@ -372,7 +354,6 @@ void main() {
test("for workpolicy ${backOffPolicy.first}", () {
expect(
JsonMapperHelper.toRegisterMethodArgument(
- isInDebugMode: true,
uniqueName: "uniqueName",
taskName: "taskName",
initialDelay: Duration(seconds: 1),
@@ -380,7 +361,6 @@ void main() {
backoffPolicy: backOffPolicy.first as BackoffPolicy,
),
{
- 'isInDebugMode': true,
'uniqueName': 'uniqueName',
'taskName': 'taskName',
'tag': null,