Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BGProcessingTask not getting executed

Tags:

xcode

ios

swift

So I created a background service, it seems to get scheduled but not executed. Background Modes I added "Background fetch" and "Background processing" to the app and added the required background modes and permitted background task scheduler identifier Info.plist

When closing the App the background process is getting scheduled but never executed (it seems like). When "Simulating background fetch" with Xcode, nothing happens or I get this error: nw_read_request_report [C2] Receive failed with error "Software caused connection abort"

Here is my AppDelegate.swift :

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        print("reguster")
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "id.myRefreshName", using: nil) { (task) in
            guard let bgTask = task as? BGProcessingTask else {
                return
            }
            self.handleAppRefreshTask(task: bgTask)
        }
        return true
    }
    
    func handleAppRefreshTask(task: BGProcessingTask) {
        let taskCompletionHandler: (Bool) -> Void = { success in
                // Call the completion handler provided by the system
                task.setTaskCompleted(success: success)
                print("Task completed with success: \(success)")
            }
        
        task.expirationHandler = {
            taskCompletionHandler(false)
            print("no success")
        }
        
        NetworkManager.getData { result in
            
            switch result {
            case .success(let hasInternet):
                DispatchQueue.main.async {
                    if hasInternet {
                        let time = Utils.getCurrentTime()
                        UserDefaults.standard.set(time, forKey: "lastUpdate")
                    } else {
                        print("No internet connection")
                    }
                }
                taskCompletionHandler(true)
            case .failure(let error):
                print("Data update failed: \(error.localizedDescription)")
                taskCompletionHandler(false)
            }
        }
        scheduleBackgroundFetch()
    }
    
    
    func scheduleBackgroundFetch() {
        let fetchTask = BGProcessingTaskRequest(identifier: "id.myRefreshName")
        fetchTask.earliestBeginDate = Date(timeIntervalSinceNow: 5)
        do {
            try BGTaskScheduler.shared.submit(fetchTask)
        } catch {
            print("Unable to submit task: \(error.localizedDescription)")
        }
    }
    
    
    // MARK: UISceneSession Lifecycle
    
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }
    
    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }
    
    func applicationWillTerminate(_ application: UIApplication) {
        Statistics.storeEvent("closeApp")
    }
    
    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        let task = BGProcessingTaskRequest(identifier: "id.myRefreshName")
        do {
            try BGTaskScheduler.shared.submit(task)
            completionHandler(.newData)
        } catch {
            completionHandler(.failed)
        }
    }
    
}

In the NetworkManager.getData function I parse a website, get its data and store it in CoreData.

like image 896
Nicolas Avatar asked Apr 17 '26 17:04

Nicolas


1 Answers

Background fetch and background processing are two different things. Background fetch is the older mechanism, but confusingly the "Background fetch" capability is used to enable both the older mechanism and the new BGAppRefreshTask mechanism.

The menu you are using in the simulator triggers the older, deprecated background fetch mechanism. It will result in a call to application(_:performFetchWithCompletionHandler:) in your App Delegate, if you have implemented that function.

There is a different process to trigger a BGAppRefreshTask or a BGProcessingTask during development.

Essentially, you need to:

  • Set a breakpoint at a line after your task has been submitted
  • Use the debugger to call a private function to trigger your task: e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"TASK_IDENTIFIER"], substituting the appropriate task identifier. In your case this would be e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"id.myRefreshName"]
  • Resume execution of your app

I note that you are scheduling a BGProcessingTask; This probably isn't what you want. BGProcessingTask is intended for long data updates and app maintenance. This sort of task only executes when the device is idle and is cancelled if the user starts using their device.

You probably want a BGAppRefreshTask, which is intended for smaller updates but doesn't require the device to be idle.

like image 89
Paulw11 Avatar answered Apr 19 '26 05:04

Paulw11