Background Processing

作者 dpearson26998d90fd121a26無授權條款1.1K 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

Schedule and execute background work on iOS using BGTaskScheduler. Use when registering BGAppRefreshTask for short background fetches, BGProcessingTask for long-running maintenance, BGContinuedProcessingTask (iOS 26+) for foreground-started work that continues in background, background URLSession downloads, or background push notifications. Covers Info.plist configuration, expiration handling, task completion, and debugging with simulated launches.

AI 產生的概覽

指導 iOS 背景工作:BGTaskScheduler、背景 URLSession 下載與背景推播通知。

功能
此技能提供在 iOS 上使用 BackgroundTasks 框架註冊、排程與執行背景工作的說明與程式碼模式。內容涵蓋 Info.plist 設定、BGAppRefreshTask、BGProcessingTask、iOS 26+ 的 BGContinuedProcessingTask、背景 URLSession 下載以及背景推播觸發。它也列出常見錯誤與審查清單,並指向包含延伸模式與除錯指引的參考檔案。
適用情境
適用於新增或審查 iOS 背景執行邏輯,例如註冊重新整理或處理任務、讓從前景啟動的工作在背景繼續,或處理背景下載與無聲推播。也適合檢查 Info.plist 識別碼、到期處理與任務完成路徑。
執行需求
不隨附指令碼,僅為說明文件。需要 iOS 或 iPadOS 應用程式專案、BackgroundTasks 框架以及 Xcode 能力設定;若使用推播觸發還需 APNs 設定。參考檔案 references/background-task-patterns.md 需與 SKILL.md 搭配閱讀。

Background Processing

Register, schedule, and execute background work on iOS using the BackgroundTasks framework, background URLSession, and background push notifications.

Contents

Info.plist Configuration

Every task identifier must be declared in Info.plist under BGTaskSchedulerPermittedIdentifiers, or submit(_:) throws BGTaskScheduler.Error.Code.notPermitted.

xml
<key>BGTaskSchedulerPermittedIdentifiers</key><array>    <string>com.example.app.refresh</string>    <string>com.example.app.db-cleanup</string>    <string>com.example.app.export.*</string></array>

Also enable the required UIBackgroundModes:

xml
<key>UIBackgroundModes</key><array>    <string>fetch</string>       <!-- Required for BGAppRefreshTask -->    <string>processing</string>  <!-- Required for BGProcessingTask --></array>

In Xcode: target > Signing & Capabilities > Background Modes > enable "Background fetch" and "Background processing".

BGTaskScheduler Registration

Register handlers before app launch completes. In UIKit, register in application(_:didFinishLaunchingWithOptions:); in SwiftUI, register in App.init().

UIKit Registration

swift
import BackgroundTasks
@mainclass AppDelegate: UIResponder, UIApplicationDelegate {    func application(        _ application: UIApplication,        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?    ) -> Bool {        BGTaskScheduler.shared.register(            forTaskWithIdentifier: "com.example.app.refresh",            using: nil  // nil = default background queue        ) { task in            self.handleAppRefresh(task: task as! BGAppRefreshTask)        }
        BGTaskScheduler.shared.register(            forTaskWithIdentifier: "com.example.app.db-cleanup",            using: nil        ) { task in            self.handleDatabaseCleanup(task: task as! BGProcessingTask)        }
        return true    }}

SwiftUI Registration

swift
import SwiftUIimport BackgroundTasks
@mainstruct MyApp: App {    init() {        BGTaskScheduler.shared.register(            forTaskWithIdentifier: "com.example.app.refresh",            using: nil        ) { task in            BackgroundTaskManager.shared.handleAppRefresh(                task: task as! BGAppRefreshTask            )        }    }
    var body: some Scene {        WindowGroup { ContentView() }    }}

BGAppRefreshTask Patterns

Short-lived tasks (~30 seconds) for fetching small data updates. The system decides when to launch; earliestBeginDate is only a lower-bound hint.

swift
func scheduleAppRefresh() {    let request = BGAppRefreshTaskRequest(        identifier: "com.example.app.refresh"    )    request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)    do {        try BGTaskScheduler.shared.submit(request)    } catch {        print("Could not schedule app refresh: \(error)")    }}
func handleAppRefresh(task: BGAppRefreshTask) {    // Schedule the next refresh before doing work    scheduleAppRefresh()
    let fetchTask = Task {        do {            let data = try await APIClient.shared.fetchLatestFeed()            await FeedStore.shared.update(with: data)            task.setTaskCompleted(success: true)        } catch {            task.setTaskCompleted(success: false)        }    }
    // CRITICAL: Handle expiration -- system can revoke time at any moment    task.expirationHandler = {        fetchTask.cancel()        task.setTaskCompleted(success: false)    }}

BGProcessingTask Patterns

Long-running tasks (minutes) for maintenance, data processing, or cleanup. They run while the device is idle and can require external power; the same earliestBeginDate lower-bound rule applies.

swift
func scheduleProcessingTask() {    let request = BGProcessingTaskRequest(        identifier: "com.example.app.db-cleanup"    )    request.requiresNetworkConnectivity = false    request.requiresExternalPower = true    request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60)    do {        try BGTaskScheduler.shared.submit(request)    } catch {        print("Could not schedule processing task: \(error)")    }}
func handleDatabaseCleanup(task: BGProcessingTask) {    scheduleProcessingTask()
    let cleanupTask = Task {        do {            try await DatabaseManager.shared.purgeExpiredRecords()            try await DatabaseManager.shared.rebuildIndexes()            task.setTaskCompleted(success: true)        } catch {            task.setTaskCompleted(success: false)        }    }
    task.expirationHandler = {        cleanupTask.cancel()        task.setTaskCompleted(success: false)    }}

BGContinuedProcessingTask (iOS 26+)

A task initiated in the foreground by a user action that continues running in the background. The system displays progress via a Live Activity. Conforms to ProgressReporting.

Availability: iOS 26.0+, iPadOS 26.0+

Unlike BGAppRefreshTask and BGProcessingTask, this task starts immediately from the foreground. The system can terminate it under resource pressure, prioritizing tasks that report minimal progress first. Set expirationHandler for user or system cancellation, cancel in-flight work, and clean up partial output before reporting completion.

swift
import BackgroundTasks
func startExport() {    // Register the task handler at app launch, not here.    // BGTaskScheduler requires registration before app launch completes.    let jobID = UUID().uuidString    let request = BGContinuedProcessingTaskRequest(        identifier: "com.example.app.export.\(jobID)",        title: "Exporting Photos",        subtitle: "Processing 247 items"    )    // Use a permitted base wildcard identifier: com.example.app.export.*    // earliestBeginDate is ignored for continued processing requests.    // .queue: begin as soon as possible if can't run immediately    // .fail: fail submission if can't run immediately    request.strategy = .queue
    do {        try BGTaskScheduler.shared.submit(request)    } catch {        print("Could not submit continued processing task: \(error)")    }}
func performExport(task: BGContinuedProcessingTask) async {    let items = await PhotoLibrary.shared.itemsToExport()    let progress = task.progress    progress.totalUnitCount = Int64(items.count)
    for (index, item) in items.enumerated() {        if Task.isCancelled { break }
        await PhotoExporter.shared.export(item)        progress.completedUnitCount = Int64(index + 1)
        // Update the user-facing title/subtitle        task.updateTitle(            "Exporting Photos",            subtitle: "\(index + 1) of \(items.count) complete"        )    }
    task.setTaskCompleted(success: !Task.isCancelled)}

For GPU work, check support and enable Background GPU Access (com.apple.developer.background-tasks.continued-processing.gpu):

swift
let supported = BGTaskScheduler.supportedResourcesif supported.contains(.gpu) {    request.requiredResources = .gpu}

Background URLSession Downloads

Use URLSessionConfiguration.background for downloads that continue even after the app is suspended or terminated. The system handles the transfer out of process.

swift
class DownloadManager: NSObject, URLSessionDownloadDelegate {    static let shared = DownloadManager()
    private lazy var session: URLSession = {        let config = URLSessionConfiguration.background(            withIdentifier: "com.example.app.background-download"        )        config.isDiscretionary = true        config.sessionSendsLaunchEvents = true        return URLSession(configuration: config, delegate: self, delegateQueue: nil)    }()
    func startDownload(from url: URL) {        let task = session.downloadTask(with: url)        task.earliestBeginDate = Date(timeIntervalSinceNow: 60)        task.resume()    }
    func urlSession(        _ session: URLSession,        downloadTask: URLSessionDownloadTask,        didFinishDownloadingTo location: URL    ) {        // Move file from tmp before this method returns        let dest = FileManager.default.urls(            for: .documentDirectory, in: .userDomainMask        )[0].appendingPathComponent("download.dat")        try? FileManager.default.moveItem(at: location, to: dest)    }
    func urlSession(        _ session: URLSession,        task: URLSessionTask,        didCompleteWithError error: (any Error)?    ) {        if let error { print("Download failed: \(error)") }    }}

Handle app relaunch — store and invoke the system completion handler:

swift
// In AppDelegate:func application(    _ application: UIApplication,    handleEventsForBackgroundURLSession identifier: String,    completionHandler: @escaping () -> Void) {    backgroundSessionCompletionHandler = completionHandler}
// In URLSessionDelegate — call stored handler when events finish:func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {    Task { @MainActor in        self.backgroundSessionCompletionHandler?()        self.backgroundSessionCompletionHandler = nil    }}

Background Push Triggers

Silent push notifications wake your app briefly to fetch new content. Set content-available: 1 in the push payload.

json
{ "aps": { "content-available": 1 }, "custom-data": "new-messages" }

Send the APNs request with apns-push-type: background and apns-priority: 5. Background push delivery is low priority and not guaranteed; keep sends infrequent, generally no more than two or three per hour.

Handle in AppDelegate:

swift
func application(    _ application: UIApplication,    didReceiveRemoteNotification userInfo: [AnyHashable: Any],    fetchCompletionHandler completionHandler:        @escaping (UIBackgroundFetchResult) -> Void) {    Task {        do {            let hasNew = try await MessageStore.shared.fetchNewMessages()            completionHandler(hasNew ? .newData : .noData)        } catch {            completionHandler(.failed)        }    }}

Enable "Remote notifications" in Background Modes and register:

swift
UIApplication.shared.registerForRemoteNotifications()

Common Mistakes

1. Missing Info.plist identifiers

swift
// DON'T: Submit a task whose identifier isn't in BGTaskSchedulerPermittedIdentifierslet request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")try BGTaskScheduler.shared.submit(request)  // Throws .notPermitted
// DO: Add every identifier to Info.plist BGTaskSchedulerPermittedIdentifiers// <string>com.example.app.refresh</string>

2. Not calling setTaskCompleted(success:)

Use the canonical app-refresh or processing handler above: every success, failure, and cancellation path reports completion exactly once.

3. Ignoring the expiration handler

Use the same canonical handler to cancel in-flight work and report failure from expirationHandler.

4. Scheduling too frequently

The scheduling sections own the lower-bound rule. Avoid minute-scale refresh requests; the system still chooses actual launch time.

5. Over-relying on background time

swift
// DON'T: Start a 10-minute operation assuming it will finishfunc handleRefresh(task: BGAppRefreshTask) {    Task { await tenMinuteSync() }}
// DO: Design work to be incremental and cancellablefunc handleRefresh(task: BGAppRefreshTask) {    let work = Task {        for batch in batches {            try Task.checkCancellation()            await processBatch(batch)            await saveBatchProgress(batch)        }        task.setTaskCompleted(success: true)    }    task.expirationHandler = {        work.cancel()        task.setTaskCompleted(success: false)    }}

Review Checklist

  • All task identifiers listed in BGTaskSchedulerPermittedIdentifiers
  • Required UIBackgroundModes enabled (fetch, processing)
  • Tasks registered before app launch completes
  • setTaskCompleted(success:) called on every code path
  • expirationHandler set and cancels in-flight work
  • Next task scheduled inside the handler (re-schedule pattern)
  • earliestBeginDate uses reasonable intervals and is treated as a hint
  • Background URLSession uses delegate (not async/closures)
  • Background URLSession file moved in didFinishDownloadingTo before return
  • handleEventsForBackgroundURLSession stores and calls completion handler
  • Background push payload includes content-available: 1
  • Background push APNs request uses apns-push-type: background and apns-priority: 5
  • fetchCompletionHandler called promptly with correct result
  • BGContinuedProcessingTask reports progress via ProgressReporting
  • Work is incremental and cancellation-safe (Task.checkCancellation())
  • No blocking synchronous work in task handlers

References

來源與署名

來源:dpearson2699/swift-ios-skills位於skills/background-processing提交8d90fd1

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 dpearson2699/swift-ios-skills 的技能

Widgetkit

dpearson2699

指導實作、審查與改進 iOS、iPadOS、watchOS 與 CarPlay 上的 WidgetKit 小工具與控制項。

Software Development1.1K2 個月前更新

Weatherkit

dpearson2699

指導 iOS 開發者使用 WeatherService 取得 WeatherKit 預報、警報與署名資訊。

Software Development1.1K2 個月前更新

Vision Framework

dpearson2699

Implement computer vision features including text recognition (OCR), face detection, barcode scanning, image segmentation, object tracking, and document scanning in iOS apps. Covers both the modern Swift-native Vision API (iOS 18+) and legacy VNRequest patterns, VisionKit DataScannerViewController for live camera scanning, and CoreMLRequest/VNCoreMLRequest for custom model inference. Use when adding OCR, barcode scanning, face detection, or custom Core ML model inference with Vision.

待分類1.1K2 個月前更新

Tipkit

dpearson2699

Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup and CloudKit tip sync; avoid for generic SwiftUI navigation or layout outside tip presentation.

待分類1.1K2 個月前更新

Tabletopkit

dpearson2699

指導使用 TabletopKit 在 visionOS 上打造多人空間桌遊,涵蓋棋具、座位、動作與 RealityKit 算繪。

Software Development1.1K2 個月前更新

Swiftui Webkit

dpearson2699

指導在 iOS 26 及更新版本的 SwiftUI App 中使用 WebKit for SwiftUI 嵌入與控制網頁內容。

Software Development1.1K2 個月前更新