iosswiftbgtaskscheduler

Silent failure when scheduling a background task


I am working on adding some background processing.

I register for background tasks and then schedule a single task.

I am seeing the following results:

I have:

<key>UIBackgroundModes</key>
<array>
    <string>processing</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
  <string>com.myApp.task</string>
</array>

I see the following in the logs:

🫥 Beginning background monitoring
🫥 Background monitoring setup complete
🫥 Submitting new background task for execution after 60.0 seconds
🫥 Request <BGAppRefreshTaskRequest: com.myApp.task, earliestBeginDate: 2025-02-17 10:59:20 +0000> submitted successfully
🫥 Pending task requests: 0 []

From the following code:


extension AlarmViewModel {
#if DEBUG
    private static let minRefreshInterval: TimeInterval = 60
#else
    private static let minRefreshInterval: TimeInterval = 60 * 60
#endif
    private static let taskIdentifier = "com.myApp.task"
    private static let tzIdDefaultsKey = "lastTimezoneIdentifier"
    func beginBackgroundMonitoring() {
        print("🫥 Beginning background monitoring")
        
        let success = BGTaskScheduler.shared.register(forTaskWithIdentifier: Self.taskIdentifier, using: .global(qos: .background)) { [weak self] task in
            print("🫥 Handling background task")
            
            guard let task2 = task as? BGAppRefreshTask else {
                print("🫥 Could not cast task to BGAppRefreshTask")
                return
            }
            task2.expirationHandler = {
                print("🫥 Expiration handler called")
            }
            self?.handleBackgroundTask(task: task2)
        }
        guard success else {
            print("🫥 Failed to register for background task")
            return
        }
        
        print("🫥 Background monitoring setup complete")
        
        submitNewBackgroundTask(after: Self.minRefreshInterval)
        
        BGTaskScheduler.shared.getPendingTaskRequests { requests in
            print("🫥 Pending task requests: \(requests.count) \(requests)")
        }
    }
    
    func submitNewBackgroundTask(after: TimeInterval) {
        print("🫥 Submitting new background task for execution after \(after) seconds")
        let request = BGAppRefreshTaskRequest(identifier: Self.taskIdentifier)
        request.earliestBeginDate = Date(timeIntervalSinceNow: after)
        do {
            try BGTaskScheduler.shared.submit(request)
            print("🫥 Request \(request) submitted successfully")
        } catch {
            print("🫥 Error submitting background task request: \(error)")
        }
    }
    
    ...

Any help further debugging or understanding why the background task isn’t running would be greatly appreciated.


Solution

  • It seems you are creating BGAppRefreshTaskRequest without enabling fetch for UIBackgroundModes. I think that processing mode is for scheduling BGProcessingTask.

    This Apple Developer Forum answer explains details how BGAppRefreshTask tasks are executed by system.

    App refresh tasks are not a way to get regular execution time. Rather, the intended use case centres around user activity. The system observes the user’s behaviour and recognises that they interact with the app regularly

    So, unless you have an app that the user is checking regularly, an app refresh task won’t help you. It’s unlikely that the system will ever decide to run your task and, even if it does, that commitment will fade away over time.

    Another Apple Developer Forum answer explains nuances on system scheduling of BGProcessingTaskRequest tasks.

    The timing for BGProcessingTaskRequest can be complicated, since it deliberately tries to run your task when it "knows" (based on local usage patterns). In practice, that generally means "in the middle of the night" (~2am) while the user is asleep and the phone is charging.

    Essentially, your use case is not covered by background execution.