Post not yet marked as solved
Post marked as unsolved with 5 replies, 1,365 views
Hello everyone!
I need to run a function every X time, and after searching all over the web, I realized that the best way for that is using the Background Tasks framework.
Problem is, although I manage to successfully register and submit a Task, it never happens.
About the code, I register the tasks when the app is launched:
@main
struct PersonalEnciclopediaApp: App {
init() {
NotificationManager.shared.requestAuthorization()
BackgroundTaskManager.shared.register()
}
var body: some Scene {
WindowGroup {
BackgroundTasks()
}
}
}
The above code requests notification authorization (which is working fine), and fires the register function:
func register() {
BGTaskScheduler.shared.register(forTaskWithIdentifier: identifier, using: .main) { task in
self.handleTask(task)
}
scheduleAppRefresh()
}
The code above registers my task, schedules a new Task with the method scheduleAppRefresh() and, for what I understood, tells the system that the functions that needs to be called when the task starts is handleTask(), which is:
func handleTask(_ task: BGTask) {
scheduleAppRefresh()
show(message: "handleTask: \(task.identifier)")
let request = performRequest { error in
task.setTaskCompleted(success: error == nil)
}
task.expirationHandler = {
task.setTaskCompleted(success: false)
request.cancel()
}
}
Again, for what I understood, the handleTask method calls the method performRequest, which is:
func performRequest(completion: @escaping (Error?) -> Void) -> URLSessionTask {
show(message: "starting performRequest")
let url = URL(string: "https://httpbin.org/get")!
let task = URLSession.shared.dataTask(with: url) { _, _, error in
print("finished request")
completion(error)
}
task.resume()
return task
}
And just so that this post is complete, the show method's responsability is just showing a notification:
func show(message: String) {
let content = UNMutableNotificationContent()
content.title = "AppRefresh task"
content.body = message
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error {
print("error \(error.localizedDescription)")
}
}
}
Here is the complete class (just in case its needed):
import Foundation
import BackgroundTasks
import UserNotifications
extension BackgroundTaskManager {
static let shared = BackgroundTaskManager()
private init() { }
let identifier = "com.hsilvgar.notifications"
func register() {
BGTaskScheduler.shared.register(forTaskWithIdentifier: identifier, using: .main, launchHandler: handleTask(_:))
scheduleAppRefresh()
}
func handleTask(_ task: BGTask) {
scheduleAppRefresh()
show(message: "handleTask: \(task.identifier)")
let request = performRequest { error in
task.setTaskCompleted(success: error == nil)
}
task.expirationHandler = {
task.setTaskCompleted(success: false)
request.cancel()
}
}
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: self.identifier)
var message = "Scheduled"
do {
try BGTaskScheduler.shared.submit(request)
} catch BGTaskScheduler.Error.notPermitted {
message = "BGTaskScheduler.shared.submit notPermitted"
} catch BGTaskScheduler.Error.tooManyPendingTaskRequests {
message = "BGTaskScheduler.shared.submit tooManyPendingTaskRequests"
} catch BGTaskScheduler.Error.unavailable {
message = "BGTaskScheduler.shared.submit unavailable"
} catch {
message = "BGTaskScheduler.shared.submit \(error.localizedDescription)"
}
show(message: "scheduleAppRefresh: \(message)")
}
func show(message: String) {
let content = UNMutableNotificationContent()
content.title = "AppRefresh task"
content.body = message
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error {
print("error \(error.localizedDescription)")
}
}
}
func performRequest(completion: @escaping (Error?) -> Void) -> URLSessionTask {
show(message: "starting performRequest")
let url = URL(string: "https://httpbin.org/get")!
let task = URLSession.shared.dataTask(with: url) { _, _, error in
print("finished request")
completion(error)
}
task.resume()
return task
}
}
So the problem is, I get the "Scheduled" notification, but I never get the notification from within performRequest(), which would tell me the task was processed.
PS:
1- I have already registered "Background Modes" with "Background fetch" and "Background processing" on my App's Signing & Capabilities
2- I have already registered the Identifier on Info.plist (in BGTaskSchedulerPermittedIdentifiers)
Here is my info.plist (if needed)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.hsilvgar.notifications</string>
</array>
<key>UIApplicationExitsOnSuspend</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
</dict>
</plist>
Does anyone know what am I missing, or what is wrong?