5

I am trying to setup some BackgroundTasks to run some code periodically (say once every day) while the app is closed. I believe the BackgroundTasks API on swift is just for that? (Please let me know if I'm mistaken). I followed the article here on Apple's Docs to implement it, and adjusting it to fit SwiftUI.

Problem: The background task never fires but is "pending" (as seen in the images)

Disclaimer: I did add the Background Modes capability and checked Background fetch and also Background processing, and added the identifer to the Info.plist

Code:

Main - Setup app delegate, get scene, set UserDefaults counter, button to get all pending tasks, text showing the UserDefault counter, Button to add to UserDefault counter, call the schedule() task when app enters background

import SwiftUI
import BackgroundTasks

@main
struct GyfterApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @Environment(\.scenePhase) var scene
    @AppStorage("test") var test = 1
    
    var body: some Scene {
        WindowGroup {
            VStack {
                Button("Test") {
                    BGTaskScheduler.shared.getPendingTaskRequests { all in
                        print("Pending Tasks Requests", all)
                    }
                }
                Text("\(test)")
                Button("ADD") {
                    test = test + 1
                }
            }
                .onChange(of: scene) { newValue in
                    switch newValue {
                    case .background:
                        print("Entered Background")
                        appDelegate.schedule()
                    default:
                        break
                    }
                }
        }
    }
}

Operation - Operation for the BackgroundTasks, it prints a message and adds 1 to the UserDefaults counter

class OP: Operation {
    @AppStorage("test") var test = 1
    
    override func main() {
        print("OPERATION RAN")
        test = test + 1
    }
}

AppDelegate -

  • Register task with given identifier, print message when it runs, and print the output of the register call to see if it was registered (it gets registered but doest not call handleSchedule(task:))
  • Schedule the task request with identifier, for 60 seconds ahead, submit to the scheduler (schedule() only gets called when app enters background, not when handleSchedule(task:) is supposed to fire which never gets called)
  • handleSchedule(task:) print statement, call schedule(), create OperationQueue, create Operation, set expirationHandler and setTaskCompleted, add operation to the queue
class AppDelegate: NSObject, UIApplicationDelegate {
    
    @AppStorage("test") var test = 1
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print("LAUNCHED")
        let a = BGTaskScheduler.shared.register(forTaskWithIdentifier: "IDENTIFIER", using: nil) { task in
            print("REGISTERED")
            self.test = self.test + 1
            self.handleSchedule(task: task as! BGAppRefreshTask)
        }
        print(a)
        return true
    }
    
     func schedule() {
        let request = BGAppRefreshTaskRequest(identifier: "IDENTIFIER")
        request.earliestBeginDate = Date(timeIntervalSinceNow: 60)
        
       do {
          try BGTaskScheduler.shared.submit(request)
       } catch {
          print("Could not schedule app refresh: \(error)")
       }
    }
    
     func handleSchedule(task: BGAppRefreshTask) {
        print("HANDLING SCHEDULE")
        schedule()
        
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        
        let operation = OP()

        
        task.expirationHandler = {
            print("BG Task Expired")
            queue.cancelAllOperations()
        }
        
        operation.completionBlock = {
            print("OPERATION COMPLETED")
            task.setTaskCompleted(success: !operation.isCancelled)
        }
        
        queue.addOperation(operation)
    }
}

Console log: Display of the print statements from the function calls

  • Line 1: App launched
  • Line 2: BGTaskScheduler "registered" (supposedly) the task with identifier
  • Line 3: Array of the pending tasks
  • Line 4: App entered background (and schedule() got called)
  • Line 5: Array of the pending tasks
LAUNCHED
true
Pending Tasks Requests []
Entered Background
Pending Tasks Requests [<BGAppRefreshTaskRequest: INDENTIFIER, earliestBeginDate: 2022-02-28 02:57:50 +0000>]
BlueStarXD
  • 73
  • 6

0 Answers0