BGTaskScheduler is an Apple framework for scheduling and executing background tasks in iOS 13 and later. It replaced the deprecated Background Fetch and performFetch, providing a unified API for handling background operations. According to Apple Developer Documentation, 2026, the framework includes two types of tasks: BGProcessingTask for long-running operations and BGAppRefreshTask for short content updates.
Key Takeaways
BGTaskScheduler is an Apple system framework introduced in iOS 13 that centrally manages the execution of background tasks. Before its introduction, developers used UIApplication backgroundTasks, performFetch, and event handling in appDelegate, which led to code fragmentation and unpredictable behavior.
The framework operates on a deferred scheduling principle: the app registers tasks with unique identifiers, and iOS itself determines the optimal moment to execute them. The system takes into account battery level, user activity, network state, and other factors.
Key capabilities include working with both short and long background operations. Unlike AlarmManager in Android, BGTaskScheduler does not guarantee exact execution time — the system reserves the right to delay a task if conditions are unfavorable.
BGTaskScheduler uses a handler-based architecture. The app registers a handler for each task type, and the system calls it when the right moment arrives. The framework itself does not execute the task directly — it merely notifies the app that it is time to run it.
Registration begins by declaring the task identifier in Info.plist through the BGTaskSchedulerPermittedIdentifiers array. Then, in the app code, the registerHandler(forTaskWithIdentifier:) method is called with a handler closure.
import BackgroundTasks
let taskID = "com.example.app.refresh"
BGTaskScheduler.shared.registerHandler(
forTaskWithIdentifier: taskID,
using: DispatchQueue.global()
) { task in
task.expirationHandler = {
// called upon force termination
}
processBackgroundTask(task as! BGAppRefreshTask)
}
After registration, the app must explicitly request task execution via submitTaskRequest. The request contains the task identifier and the earliest possible launch date. The system saves the request and processes it when it deems conditions suitable.
let request = BGAppRefreshTaskRequest(
identifier: taskID
)
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Scheduling error: \(error)")
}
BGTaskScheduler provides two main types of tasks, each designed for its own use case. Choosing the right type directly affects the probability of successful task execution by the system.
BGAppRefreshTask is designed for short background content updates: loading new data, synchronizing with the server, updating widgets. Execution time is limited to 30 seconds, after which the system forcibly terminates the task. This type of task runs more frequently than BGProcessingTask and has higher priority.
BGProcessingTask is designed for longer operations: processing media files, indexing Core Data, creating backups. The task can run for up to several minutes, but the system launches it less frequently and only under favorable conditions — connected to power, stable Wi-Fi, and low device load.
| Parameter | BGAppRefreshTask | BGProcessingTask |
|---|---|---|
| Time Limit | 30 seconds | several minutes |
| Launch Frequency | high | low |
| Conditions | any | power + Wi-Fi |
| Requires power | no | recommended |
| Example | feed update | video processing |
Correct registration is a mandatory requirement for BGTaskScheduler to work. If a task is not registered in Info.plist, the system will ignore any request to execute it.
The Info.plist file must include the BGTaskSchedulerPermittedIdentifiers array with a list of string identifiers. Each identifier must be unique within the app. Apple recommends using reverse domain notation.
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.example.app.refresh</string>
<string>com.example.app.processing</string>
</array>
To schedule a task, use the submitTaskRequest method. If a task is no longer needed, it can be cancelled via cancelTaskRequest or cancelAllTaskRequests. The system also automatically cancels tasks when the app is deleted or data is reset.
BGTaskScheduler provides the ability to track the state of scheduled tasks through getPendingTaskRequests. This method returns a list of all active requests with information about their type, identifier, and earliestBeginDate. For each request, you can check whether it has already been completed or cancelled, and decide whether to reschedule.
It is important to note that the system does not provide a direct callback about the success of a background task — the handler itself must report the result via the task properties. setTaskCompleted allows marking a task as successfully completed, after which the system can launch the next scheduled task of this type. If a task does not call setTaskCompleted, the system considers it completed upon timeout or forced termination.
For diagnostics, it is recommended to use OSLog in the handler and view logs through Console.app on Mac. Apple also provides the MetricKit tool for analyzing background task performance — it collects data on execution time, energy consumption, and launch frequency that can be used for optimization.
// Cancel specific task
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: taskID)
// Cancel all tasks
BGTaskScheduler.shared.cancelAllTaskRequests()
// Check scheduled tasks
BGTaskScheduler.shared.getPendingTaskRequests { requests in
print("Scheduled \(requests.count) tasks")
}
BGTaskScheduler imposes strict limitations on background work. The system may delay a task indefinitely if conditions are unfavorable. Developers must understand that the framework is not intended for real-time tasks.
Key limitations include: the system does not guarantee task execution at the specified time, the maximum number of concurrent tasks is limited, and energy consumption is strictly controlled. Running multiple tasks in succession may result in them being merged or cancelled.
To increase the likelihood of execution, it is recommended to set earliestBeginDate no earlier than 1 hour for BGProcessingTask and 15 minutes for BGAppRefreshTask. It is also important to handle expirationHandler — if a task cannot meet its time limit, the system calls this handler for proper termination. Rescheduling should be done inside the handler itself to maintain a continuous background work cycle.
Another important limitation concerns network requests. BGTaskScheduler does not guarantee an active network connection during task execution. The app must independently check network availability via NWPathMonitor and defer processing if the connection is absent. This differs from Android JobScheduler, which can activate a task only when connected to a specific type of network. In practice, developers often combine BGTaskScheduler with background NSURLSession URL sessions for reliable data loading.
Starting with macOS Catalina, BGTaskScheduler is also available on Mac. This allows creating cross-platform background tasks for UIKit apps running on Apple Silicon. On watchOS, the framework has limited functionality — only short BGAppRefreshTask are available for updating complications and synchronizing data with iPhone. Developers should account for platform differences when planning background architecture.
Apple provides several tools for debugging BGTaskScheduler. The command e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.task"] in lldb forcibly launches a background task, bypassing system limitations. Xcode has a Simulate Background Fetch flag in the Debug menu that emulates a short background update. MetricKit is used for performance analysis — it collects information about launch frequency, execution duration, and energy consumption of each task. This data helps optimize scheduling frequency and choose the right task type.
In practice, BGTaskScheduler is well suited for updating widget data, iCloud sync, processing push notifications with content, and indexing for Spotlight search. It is not suitable for real-time analytics, chat applications, or any tasks requiring immediate execution.
For in-depth study of BGTaskScheduler, Apple recommends official WWDC documentation: the "Advances in Background Tasks" session (2020) covers migration from deprecated APIs, and "Background Tasks in Practice" (2021) contains real-world usage cases. The Energy Efficiency Guide section is also useful, describing how the framework fits into Apple's overall energy-saving strategy. Code examples are available in the official Apple Developer repository on GitHub with complete projects for iOS and macOS.
Frequently Asked Questions
Background Fetch was limited to one background task per app and had no priority mechanism. BGTaskScheduler supports multiple tasks with different types, provides a unified API, and automatic energy management.
Apple does not set an explicit limit on the number of registered identifiers, but in practice it is recommended to use no more than 5–10 tasks. A larger number reduces the probability of each specific task being executed due to competition for system resources.
For debugging, use the command e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.task"] in lldb. It forcibly launches a task, bypassing system limitations. The Xcode Simulate Background Fetch flag in the Debug menu is also available.
Yes, BGTaskScheduler can launch a process even if the app was force-closed by the user. However, the system may apply additional delays, and not all task types guarantee execution in this scenario.
The system calls expirationHandler, signaling to the task that it needs to finish. If the app ignores this signal and continues working, iOS forcibly terminates the process. After that, the system may reduce the priority of all the app's background tasks.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also