I've got an iOS app that performs a series of operations when initialized and when a refresh is performed.
In short, that app has:
-
An obervable object (appManager) that houses variables that act as "state" for the app.
-
Methods in the observable object that perform a variety of operations.
Here's an example:
- Perform an API operation to get tokens to perform API actions on remote API service.
- Perform a fetch on an external database to retrieve some data.
- Decode that data an place the data into @Published variables, using (DispatchQueue.main.async).
- Perform another remote API fetch operation on a different endpoint.
- Put more variables in "state"
So when the user opens the app and after init runs, they see what they expect to see. And when they tap a "refresh" button, another function (e.g. getUpdatedAppData()
) is called with similar steps to the above, and the app updates the published variables and the view(s) update.
The above steps are all performed using async functions, using Swift's new async/await functionality. Everything works swimmingly when the app is in the foreground.
What I'd like to do is perform the updateAppData()
function using Background tasks as described here..
I have successfully setup the app to register and schedule a task, using BGTaskScheduler, and I can successfully emulate the triggerring of that task by setting a breakpoint and running the following code in the console:
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.task"]
If the background tasks is something simple, like printing to the console, it seems to work
private func configureBackgroundTasks() {
Logger.log(.info, "Registering background tasks...")
let bgTaskIdentifier = "com.example.task"
BGTaskScheduler.shared.register(forTaskWithIdentifier: bgTaskIdentifier, using: DispatchQueue.main) { (task) in
Logger.log(.info, "Performing background task \(bgTaskIdentifier)")
task.setTaskCompleted(success: true)
backgroundTaskManager.scheduleAppRefresh()
}
}
However, if the function is something like the below, I can see the function being triggered, when simulating the running of the task, but the function hangs at the start of the first async operation (e.g. fetch tokens above).
It seems to "try to complete" (and usuually fails) once the app is brought to the foreground, but that's obviously not what I want to happen.
Surely, I'm misunderstanding something and/or doing something wrong, so I'm hoping to get some help here.
Thanks in advance for any assistance toward achieving what I'm trying to do here.
private func configureBackgroundTasks() {
Logger.log(.info, "Registering background tasks...")
let bgTaskIdentifier = "com.example.task"
BGTaskScheduler.shared.register(forTaskWithIdentifier: bgTaskIdentifier, using: DispatchQueue.main) { (task) in
Logger.log(.info, "Performing background task \(bgTaskIdentifier)")
Task.init {
if let updatedData = await appManager.getUpdateAppData() {
DispatchQueue.main.async {
appManager.data = updatedData
}
}
}
task.setTaskCompleted(success: true)
backgroundTaskManager.scheduleAppRefresh()
}
}