Background Taskは、アプリケーションがバックグラウンドに移行した後に操作を完了できるようにするiOSの仕組みです。システムはタスクの実行に制限時間(最大30秒)を割り当て、それを過ぎるとアプリケーションは強制的に中断されます。Apple(2026年)によると、 expiration handlerの使用はバックグラウンドタスクを正しく完了するための必須要件です。
重要ポイント
Background Taskは、アプリケーションがバックグラウンドに移行した後に実行時間を延長できるようにするプログラム可能なiOSメカニズムです。ユーザーがアプリを最小化すると、システムはUIApplicationDelegate.applicationDidEnterBackgroundを呼び出します。その時点で重要な操作(データの保存、リクエストの送信)が実行中の場合は、アプリケーションはbeginBackgroundTaskを介して追加の時間を要求できます。
Background Taskを使用しない場合、アプリケーションはバックグラウンドに入ってから3~5秒以内に強制的に中断されます。未完了の操作はすべて中断され、データは保存されず、ネットワークリクエストは中断され、状態は失われます。Background Taskは、これらの操作を正しく完了するために最大30秒の時間をアプリケーションに提供します。
beginBackgroundTaskは、マルチタスクをサポートする最初のリリースであるiOS 4.0で登場しました。iOS 4以前は、ホームボタンを押すとアプリケーションは完全に終了していました。iOS 7ではBackground FetchとURLSessionのバックグラウンド設定が導入されました。iOS 13以降、Appleは新しいプロジェクトにBGTaskSchedulerを推奨していますが、beginBackgroundTaskは短期間の操作に引き続き有用です。
beginBackgroundTaskは、タスクを登録して一意のUIBackgroundTaskIdentifierを返すUIApplicationのメソッドです。システムはバックグラウンド実行タイマーを増やします。時間が切れるとexpiration handlerが呼び出され、その後アプリケーションはendBackgroundTask(identifier:)を呼び出してタスクを正しく完了する必要があります。
ステップ1 — アプリケーションがdidEnterBackground通知を受け取ります。ステップ2 — beginBackgroundTask(expirationHandler:)が呼び出されます。ステップ3 — 重要な操作(保存、ネットワークリクエスト)が実行されます。ステップ4 — 完了時にendBackgroundTask(identifier:)が呼び出されます。操作が30秒以内に完了しない場合、システムはexpiration handlerを呼び出し、アプリケーションは直ちにタスクを終了する必要があります。
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Expiration handler
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
func finishBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
backgroundTimeRemainingプロパティは、バックグラウンドタスクが強制終了されるまでの残り秒数を返します。値はリアルタイムで減少します。アプリケーションがバックグラウンドにない場合はDBL_MAXを返します。このプロパティを使用して動作を適応させます:残り5秒未満になったら、長時間の操作を中断して進捗を保存します。
アプリケーションは複数のBackground Tasksを同時に登録できます。各タスクは独自の識別子を取得します。合計実行時間は累積されます — 3つのタスクが登録されている場合、アプリケーションは最大90秒を取得できます。ただし、リソースが枯渇したりバッテリーの制限を超えた場合、システムはアプリケーションを途中で終了することがあります。
Expiration handlerは、バックグラウンドタスクに割り当てられた時間が切れたときにシステムが呼び出すコードブロックです。expiration handlerを持つことはAppleの必須要件です。これがないと、アプリケーションはシステムによって強制終了され、データが失われる可能性があります。
expiration handlerは、アプリケーションの状態を保存するための最小限のアクションを実行する必要があります:現在のデータを永続ストレージに保存し、対応する識別子でendBackgroundTaskを呼び出し、識別子を.invalidに設定します。expiration handler内で新しい長時間の操作を開始することは禁止されています — 実行時間は1~2秒に制限されています。
func handleExpiration() {
// 実行進捗を保存中
saveProgressToUserDefaults()
// アクティブなネットワークリクエストをキャンセル中
currentTask?.cancel()
// Background Taskを終了中
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// Expiration handler付き登録
backgroundTaskID = UIApplication.shared.beginBackgroundTask(
withName: "SaveDocument",
expirationHandler: handleExpiration
)
最も一般的な間違いは、expiration handler内でendBackgroundTaskを呼び出さないことです。この場合、システムはタスクをアクティブと見なし続け、アプリケーションは中断状態に移行せず、バッテリーを消耗します。2つ目の間違いは、expiration handler内で長時間の操作を開始することです。システムは操作が完了する前にアプリケーションを終了する可能性があり、データが失われます。
expiration handlerでは、実行時間は極めて限られています — 通常1秒未満です。したがって、handler内では以下を実行できません:複雑な計算、ネットワークリクエスト、CoreDataへの大量データの書き込み。アトミックな操作のみ:UserDefaultsへの単一キーの書き込み、状態フラグの設定、endBackgroundTaskの呼び出し。
アプリケーションがバックグラウンドに入ったときにドキュメントを保存するための完全なBackground Task実装を見てみましょう。この例には、タスクの登録、残り時間の確認を伴う操作の実行、endBackgroundTaskによる適切な完了が含まれています。
バックグラウンドに入るとき、アプリケーションは大きなドキュメントの保存を開始します。Background Taskは操作に最大30秒を提供します。時間が切れると、expiration handlerが中間結果を保存します。保存が完了すると、リソースを解放するためにendBackgroundTaskが呼び出されます。
class DocumentManager {
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func saveDocumentInBackground(document: Document) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
self.saveProgress(document)
self.endBackgroundTask()
}
DispatchQueue.global().async {
document.save()
self.endBackgroundTask()
}
}
private func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
バックグラウンドでのネットワークリクエストは特別な注意が必要です — expiration handlerが応答を受信する前にリクエストを中断する可能性があります。dataTaskを伴うURLSessionを使用し、送信前にbackgroundTimeRemainingを確認してください。残り5秒未満の場合は、次のアプリ起動まで送信を延期してください。
func sendCriticalData(data: Data) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// 再送信用のデータを保存中
saveForRetry(data)
self.endBackgroundTask()
}
let task = URLSession.shared.dataTask(with: request) { _, _, error in
if error != nil {
saveForRetry(data)
}
self.endBackgroundTask()
}
task.resume()
}
長時間の操作では、UIApplication.shared.backgroundTimeRemainingを監視してください。値がしきい値(10秒)を下回ったら、現在の操作を中断して中間結果の保存を開始します。これにより、expiration handlerが強制的に呼び出される前に適切に完了できます。
Background Taskは強力なメカニズムですが、不適切に使用するとバッテリーやパフォーマンスの問題を引き起こします。Appleの推奨事項とコミュニティで実証されたプラクティスに従うことで、一般的な間違いを回避し、安定したアプリケーション動作を確保できます。
Background Taskをできるだけ早く完了してください。バックグラウンド実行の1秒ごとにバッテリーを消費します。最適な時間 — 5秒未満。操作にそれ以上かかる場合は、BGTaskSchedulerまたはBackground Modesの使用を検討してください。実際の必要性なくbeginBackgroundTask呼び出しで人為的にタスクを延長しないでください。
UIBackgroundTaskIdentifierはクラスまたは構造体のプロパティとして保存してください。グローバル変数として渡さないでください — 複数タスクで競合を引き起こします。endBackgroundTaskを呼び出した後、識別子を.invalidに設定して二重完了を防止してください。
Xcode Simulatorで、ホームボタン(Command + Shift + H)でアプリをバックグラウンドに切り替えます。Debug — Simulate Background Fetchを使用してバックグラウンド起動をテストします。時間切れをシミュレートするには、環境変数BACKGROUND_TASK_SIMULATE_EXPIRATION = YESを設定します — expiration handlerが5秒以内に呼び出されます。
// 操作開始前の残り時間を確認中
let remaining = UIApplication.shared.backgroundTimeRemaining
guard remaining > 10.0 else {
// 時間不足 — タスクを延期
scheduleForNextLaunch()
return
}
// 時間管理による操作の実行
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
saveProgress()
self.endBackgroundTask()
}
expiration handlerはselfをキャプチャします — backgroundTaskIDが同じオブジェクトのプロパティとして保存されている場合、retainサイクルが発生する可能性があります。クロージャで[weak self]を使用するか、識別子を個別に保存してください。retainサイクルはメモリリークを引き起こし、アプリケーションが終了するまでオブジェクトが解放されません。
iOS 13のリリースに伴い、AppleはBGTaskSchedulerを導入しました — 古いBackground Task APIに代わる最新のものです。どちらのメカニズムも同様のタスクを解決しますが、BGTaskSchedulerはより多くの制御と柔軟性を提供します。違いを理解することで、特定のシナリオに適したツールを選択するのに役立ちます。
| 特性 | Background Task | BGTaskScheduler |
|---|---|---|
| iOSバージョン | 4.0+ | 13.0+ |
| 最大時間 | 30秒 | 30秒 / 10分(processing) |
| 開始 | didEnterBackground | スケジューリング + システム |
| 実行保証 | バックグラウンド移行時のみ | システムの都合の良い任意の時間 |
| Expiration handler | あり、必須 | あり、task.expirationHandler経由 |
| インターネット | 起動時に必要 | requiresNetworkConnectivityで利用可能 |
| 充電 | 不要 | processingではオプション |
beginBackgroundTaskは、バックグラウンドに入るときに即座に実行する必要がある短期間の操作に適しています:状態の保存、ネットワークリクエストの完了、データのキャッシュ。APIはシンプルで、Info.plistの設定や識別子の登録は不要です。10秒未満で完了する操作に最適です。
BGTaskSchedulerは、任意の都合の良い時間に実行できるタスクに適しています:定期的な同期、キャッシュのクリーンアップ、ウィジェットの更新。システム自体がユーザーの行動とバッテリー状態を考慮して最適な時間を選択します。長時間の操作(最大10分)にはBGProcessingTaskを使用してください。
両方のAPIを同じアプリケーションで使用できます。Background Task — バックグラウンドに入るときの即時操作(下書きの保存)に。BGTaskScheduler — スケジュールされた更新(6時間ごとの同期)に。責任を分離することで、データの適切な保存とエネルギー効率の良いバックグラウンドメンテナンスを保証します。
よくある質問
はい、beginBackgroundTaskはアプリケーション実行中の任意の時点で呼び出すことができます。ただし、システムの時間割り当てはバックグラウンドに移行した後にのみ開始されます。アプリケーションがフォアグラウンドにある場合、beginBackgroundTaskを呼び出しても効果はありません — backgroundTimeRemainingはDBL_MAXを返し、タスクはバックグラウンドに入ったときにアクティブになります。
endBackgroundTaskが呼び出されない場合、システムはアプリケーションをバックグラウンドでアクティブと見なし続けます。30秒後にexpiration handlerが呼び出されますが、それにもendBackgroundTaskがない場合、アプリケーションはメモリに残り続け、バッテリーを消耗します。iOS 13+では、システムは3分後にそのようなアプリケーションを強制終了します。
標準時間は30秒です。延長できるのはBackground Modes:オーディオ(再生)、位置情報(GPS)、Bluetooth(BLE)を介してのみです。またはBGProcessingTask(iOS 13+) — 充電とWi-Fi環境で最大10分まで。beginBackgroundTask自体には制限を増やす方法はありません。
はい、iPadOSはiOSと同じ制限でbeginBackgroundTaskを完全にサポートしています。Stage Managerを搭載したiPadでは、アプリケーションはメモリに長く留まることができます — システムはRAMが多いためアプリケーションを中断する頻度が低くなります。ただし、バックグラウンドタスクの30秒制限は変わりません。
デバイスをXcodeに接続し、アプリケーションを起動して最小化すると、コンソールにシステムログが表示されます。sysdiagnoseを使用して詳細なログを収集します:デバイスからsysdiagnoseを強制トリガーします(音量上 + 音量下 + 電源)。XcodeのDebug Navigatorでバックグラウンドタスクのアクティビティを監視します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。