Skip to content

Commit 3a99805

Browse files
committed
fix(ios): pass inputData to periodic background tasks
- Store inputData in UserDefaults when registering periodic tasks - Retrieve and pass stored inputData when executing periodic tasks - Fix issue where periodic tasks received nil inputData instead of provided data - Update documentation with periodic task inputData examples Closes #612
1 parent 7f4f870 commit 3a99805

4 files changed

Lines changed: 86 additions & 13 deletions

File tree

docs/quickstart.mdx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,19 @@ Workmanager().registerPeriodicTask(
176176
"cleanup",
177177
frequency: Duration(hours: 24),
178178
);
179+
180+
// Schedule a periodic task with input data
181+
Workmanager().registerPeriodicTask(
182+
"sync-task",
183+
"data_sync",
184+
frequency: Duration(hours: 6),
185+
inputData: <String, dynamic>{
186+
'server_url': 'https://api.example.com',
187+
'sync_type': 'full',
188+
'max_retries': 3,
189+
},
190+
);
191+
```
179192
```
180193
181194
## Task Results
@@ -200,4 +213,4 @@ Your background tasks can return:
200213
201214
- **[Task Customization](customization)** - Advanced configuration with constraints, input data, and management
202215
- **[Debugging Guide](debugging)** - Learn how to debug and troubleshoot background tasks
203-
- **[Example App](https://github.com/fluttercommunity/flutter_workmanager/tree/main/example)** - Complete working demo
216+
- **[Example App](https://github.com/fluttercommunity/flutter_workmanager/tree/main/example)** - Complete working demo

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ EXTERNAL SOURCES:
3838
SPEC CHECKSUMS:
3939
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
4040
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
41-
path_provider_foundation: 608fcb11be570ce83519b076ab6a1fffe2474f05
41+
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
4242
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
4343
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
4444
workmanager_apple: 904529ae31e97fc5be632cf628507652294a0778

workmanager_apple/ios/Sources/workmanager_apple/UserDefaultsHelper.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ struct UserDefaultsHelper {
1515

1616
enum Key {
1717
case callbackHandle
18+
case periodicTaskInputData(taskIdentifier: String)
1819

1920
var stringValue: String {
20-
return "\(WorkmanagerPlugin.identifier).\(self)"
21+
switch self {
22+
case .callbackHandle:
23+
return "\(WorkmanagerPlugin.identifier).callbackHandle"
24+
case .periodicTaskInputData(let taskIdentifier):
25+
return "\(WorkmanagerPlugin.identifier).periodicTaskInputData.\(taskIdentifier)"
26+
}
2127
}
2228
}
2329

@@ -31,6 +37,16 @@ struct UserDefaultsHelper {
3137
return getValue(for: .callbackHandle)
3238
}
3339

40+
// MARK: periodicTaskInputData
41+
42+
static func storePeriodicTaskInputData(_ inputData: [String: Any]?, forTaskIdentifier taskIdentifier: String) {
43+
store(inputData, key: .periodicTaskInputData(taskIdentifier: taskIdentifier))
44+
}
45+
46+
static func getStoredPeriodicTaskInputData(forTaskIdentifier taskIdentifier: String) -> [String: Any]? {
47+
return getValue(for: .periodicTaskInputData(taskIdentifier: taskIdentifier))
48+
}
49+
3450
// MARK: Private helper functions
3551

3652
private static func store<T>(_ value: T, key: Key) {

workmanager_apple/ios/Sources/workmanager_apple/WorkmanagerPlugin.swift

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,31 @@ public class WorkmanagerPlugin: FlutterPluginAppLifeCycleDelegate, FlutterPlugin
3232
operationQueue.addOperation(operation)
3333
}
3434

35+
/// Handles execution of a periodic background task.
36+
///
37+
/// This method is called by iOS when a BGAppRefreshTask is triggered.
38+
/// It retrieves stored inputData and executes the Flutter task.
39+
///
40+
/// - Parameters:
41+
/// - identifier: Task identifier
42+
/// - task: The BGAppRefreshTask instance from iOS
43+
/// - inputData: Input data passed from the Dart side (may be nil)
3544
@available(iOS 13.0, *)
36-
public static func handlePeriodicTask(identifier: String, task: BGAppRefreshTask, earliestBeginInSeconds: Double?) {
45+
public static func handlePeriodicTask(identifier: String, task: BGAppRefreshTask, inputData: [String: Any]?) {
3746
guard let callbackHandle = UserDefaultsHelper.getStoredCallbackHandle(),
3847
let _ = FlutterCallbackCache.lookupCallbackInformation(callbackHandle)
3948
else {
4049
logError("[\(String(describing: self))] \(WMPError.workmanagerNotInitialized.message)")
4150
return
4251
}
4352

44-
// If frequency is not provided it will default to 15 minutes
45-
schedulePeriodicTask(taskIdentifier: task.identifier, earliestBeginInSeconds: earliestBeginInSeconds ?? (15 * 60))
53+
// Schedule the next occurrence (iOS will determine actual timing based on usage patterns)
54+
schedulePeriodicTask(taskIdentifier: task.identifier, earliestBeginInSeconds: 15 * 60)
4655

4756
let operationQueue = OperationQueue()
4857
let operation = createBackgroundOperation(
4958
identifier: task.identifier,
50-
inputData: nil,
59+
inputData: inputData,
5160
backgroundMode: .backgroundPeriodicTask(identifier: identifier)
5261
)
5362

@@ -57,6 +66,13 @@ public class WorkmanagerPlugin: FlutterPluginAppLifeCycleDelegate, FlutterPlugin
5766
operationQueue.addOperation(operation)
5867
}
5968

69+
/// Starts a one-off background task with the specified input data.
70+
///
71+
/// - Parameters:
72+
/// - identifier: Task identifier
73+
/// - taskIdentifier: iOS background task identifier for lifecycle management
74+
/// - inputData: Input data to pass to the Flutter task
75+
/// - delaySeconds: Delay before task execution
6076
@available(iOS 13.0, *)
6177
public static func startOneOffTask(identifier: String, taskIdentifier: UIBackgroundTaskIdentifier, inputData: [String: Any]?, delaySeconds: Int64) {
6278
let operationQueue = OperationQueue()
@@ -70,20 +86,29 @@ public class WorkmanagerPlugin: FlutterPluginAppLifeCycleDelegate, FlutterPlugin
7086
operationQueue.addOperation(operation)
7187
}
7288

89+
/// Registers a periodic background task with iOS BGTaskScheduler.
90+
///
91+
/// This method must be called during app initialization (typically in AppDelegate)
92+
/// to register the task identifier with iOS. The actual task scheduling with inputData
93+
/// happens later when called from the Dart/Flutter side.
94+
///
95+
/// - Parameters:
96+
/// - identifier: Unique task identifier that matches the one used in Dart
97+
/// - frequency: Optional frequency hint in seconds (iOS may ignore this based on usage patterns)
98+
///
99+
/// - Note: This registers the task handler only. Use Workmanager.registerPeriodicTask()
100+
/// from Dart to actually schedule the task with inputData.
73101
@objc
74102
public static func registerPeriodicTask(withIdentifier identifier: String, frequency: NSNumber?) {
75103
if #available(iOS 13.0, *) {
76-
var frequencyInSeconds: Double?
77-
if let frequencyValue = frequency {
78-
frequencyInSeconds = frequencyValue.doubleValue
79-
}
80-
81104
BGTaskScheduler.shared.register(
82105
forTaskWithIdentifier: identifier,
83106
using: nil
84107
) { task in
85108
if let task = task as? BGAppRefreshTask {
86-
handlePeriodicTask(identifier: identifier, task: task, earliestBeginInSeconds: frequencyInSeconds)
109+
// Retrieve the stored inputData for this periodic task
110+
let storedInputData = UserDefaultsHelper.getStoredPeriodicTaskInputData(forTaskIdentifier: task.identifier)
111+
handlePeriodicTask(identifier: identifier, task: task, inputData: storedInputData)
87112
}
88113
}
89114
}
@@ -102,6 +127,12 @@ public class WorkmanagerPlugin: FlutterPluginAppLifeCycleDelegate, FlutterPlugin
102127
}
103128
}
104129

130+
/// Registers a background processing task with iOS BGTaskScheduler.
131+
///
132+
/// This method must be called during app initialization (typically in AppDelegate)
133+
/// to register the task identifier with iOS for background processing tasks.
134+
///
135+
/// - Parameter identifier: Unique task identifier that matches the one used in Dart
105136
@objc
106137
public static func registerBGProcessingTask(withIdentifier identifier: String) {
107138
if #available(iOS 13.0, *) {
@@ -140,6 +171,12 @@ public class WorkmanagerPlugin: FlutterPluginAppLifeCycleDelegate, FlutterPlugin
140171

141172
// MARK: - FlutterPlugin conformance
142173

174+
/// Sets the plugin registrant callback for background task execution.
175+
///
176+
/// This callback is used to register additional plugins when background tasks
177+
/// run in a separate Flutter engine instance.
178+
///
179+
/// - Parameter callback: The callback to register plugins in the background engine
143180
@objc
144181
public static func setPluginRegistrantCallback(_ callback: @escaping FlutterPluginRegistrantCallback) {
145182
flutterPluginRegistrantCallback = callback
@@ -191,6 +228,13 @@ public class WorkmanagerPlugin: FlutterPluginAppLifeCycleDelegate, FlutterPlugin
191228

192229
executeIfSupportedVoid(completion: completion, feature: "PeriodicTask") {
193230
let initialDelaySeconds = Double(request.initialDelaySeconds ?? 0)
231+
232+
// Store the inputData for later retrieval when the task executes
233+
UserDefaultsHelper.storePeriodicTaskInputData(
234+
request.inputData as? [String: Any],
235+
forTaskIdentifier: request.uniqueName
236+
)
237+
194238
WorkmanagerPlugin.schedulePeriodicTask(
195239
taskIdentifier: request.uniqueName,
196240
earliestBeginInSeconds: initialDelaySeconds

0 commit comments

Comments
 (0)