Swift Academy · レッスン

バックグラウンドタスクと更新

BGTaskSchedulerでバックグラウンド処理をスケジュールします。

レッスン 3/413 ステップ

「バックグラウンドタスクと更新」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

バックグラウンドタスクが存在する理由

iOSはバッテリーを節約するため、アプリを積極的に停止状態にします。フィードの更新やダウンロードの処理など、バックグラウンドで意味のある処理を行うには、BGTaskSchedulerを通じてスケジュールします。システムは電力消費や利用パターンとアプリの要求のバランスを取りながら、いつ実行するかを決定します。

import BackgroundTasks
// You request work; the system schedules it later
// at an opportune moment (charging, on Wi-Fi, etc.)

2種類のタスク

タスクには2種類あります。BGAppRefreshTaskは短時間(数秒)の頻繁な更新に、BGProcessingTaskはデータベースのメンテナンスなど、より長時間(数分)かかる負荷の高い処理に使用します。後者では、電源やネットワークを必要条件として指定することもできます。

import BackgroundTasks
// BGAppRefreshTask  -> quick content refresh
// BGProcessingTask  -> long maintenance, can require
//                      external power / network

識別子の宣言

各タスクには一意の識別子が必要で、Info.plistのBGTaskSchedulerPermittedIdentifiersに一覧として記述します。この宣言がないと登録に失敗します。識別子には逆引きDNS形式の文字列を使用します。

// Info.plist:
// BGTaskSchedulerPermittedIdentifiers = [
//   "com.example.app.refresh",
//   "com.example.app.cleanup"
// ]
let ids = "declare every identifier in Info.plist"
_ = ids

ハンドラーの登録

起動時に、各識別子のハンドラーをBGTaskScheduler.shared.registerで登録します。これはアプリの起動処理が完了する前に行う必要があります。ハンドラーは、後でシステムがタスクを実行すると判断したときに実行されます。

import BackgroundTasks
func registerTasks() {
    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.example.app.refresh",
        using: nil) { task in
        handleRefresh(task as! BGAppRefreshTask)
    }
}

リクエストの送信

登録するだけでは、ハンドラーを設定したことにしかなりません。実際に実行を依頼するには、BGAppRefreshTaskRequestを作成して最も早い開始日時を設定し、submitします。システムはその日時を保証ではなく目安として扱います。

import BackgroundTasks
func scheduleRefresh() {
    let request = BGAppRefreshTaskRequest(
        identifier: "com.example.app.refresh")
    request.earliestBeginDate = Date(
        timeIntervalSinceNow: 15 * 60)
    try? BGTaskScheduler.shared.submit(request)
}

必ず再スケジュールする

送信したリクエストは最大で1回しか実行されません。更新を継続するには、ハンドラーが終了する前に次のリクエストを送信する必要があります。処理の途中でクラッシュしてもサイクルが止まらないよう、先に再スケジュールしてから作業を行います。

import BackgroundTasks
func handleRefresh(_ task: BGAppRefreshTask) {
    scheduleRefresh() // queue the next run first
    // ... then perform this run's work
}

完了の通知

処理が終わったら、task.setTaskCompleted(success:)を呼び出す必要があります。これを忘れるとバックグラウンド実行時間を無駄にし、今後のタスクの優先度を下げられる可能性があります。処理が成功したかどうかを渡すことで、システムがスケジュールを調整できます。

import BackgroundTasks
func handleRefresh(_ task: BGAppRefreshTask) {
    scheduleRefresh()
    Task {
        let ok = await fetchLatest()
        task.setTaskCompleted(success: ok)
    }
}
func fetchLatest() async -> Bool { true }

有効期限ハンドラーへの対応

システムは予定より早く実行時間を回収することがあります。task.expirationHandlerに、進行中の処理をキャンセルして後片付けを行う処理を設定し、途中で打ち切られても正常に終了できるようにします。締め切りを知らせるアラームのように扱ってください。

import BackgroundTasks
func handle(_ task: BGAppRefreshTask) {
    let work = Task { await fetchLatest() }
    task.expirationHandler = {
        work.cancel() // stop before the OS suspends us
    }
}
func fetchLatest() async -> Bool { true }

必要条件付きの処理タスク

負荷の高い処理には、BGProcessingTaskRequestを使ってrequiresNetworkConnectivityとrequiresExternalPowerを設定できます。システムはこれらの条件が満たされるまで待つため、長時間のタスクを、Wi-Fiに接続して一晩充電しているデバイス上で実行できます。

import BackgroundTasks
func scheduleCleanup() {
    let req = BGProcessingTaskRequest(
        identifier: "com.example.app.cleanup")
    req.requiresExternalPower = true
    req.requiresNetworkConnectivity = false
    try? BGTaskScheduler.shared.submit(req)
}

デバッガーでのテスト

システムがスケジュールするまで何時間も待つ必要はありません。デバッガーで一時停止し、LLDBコマンドを実行して登録済みのタスクをすぐに起動すれば、ハンドラーを最初から最後まで検証できます。

// In LLDB while paused:
// e -l objc -- (void)[[BGTaskScheduler sharedScheduler]
//   _simulateLaunchForTaskWithIdentifier:
//   @"com.example.app.refresh"]
let test = "force-launch task via LLDB"
_ = test

完全な更新サイクル

一連の流れをまとめると、起動時に登録し、バックグラウンド移行時に送信します。ハンドラーでは再スケジュールし、有効期限ハンドラーで保護し、非同期処理を行って、完了を報告します。

import BackgroundTasks
func handleRefresh(_ task: BGAppRefreshTask) {
    scheduleRefresh()
    let work = Task { await fetchLatest() }
    task.expirationHandler = { work.cancel() }
    Task {
        let ok = await work.value
        task.setTaskCompleted(success: ok)
    }
}
func fetchLatest() async -> Bool { true }

クイックチェック

繰り返し実行する更新タスクのライフサイクルを思い出してください。

まとめ

バックグラウンドスケジューリングを学びました:

  • BGAppRefreshTaskは短時間の更新に、BGProcessingTaskは電源やネットワークの必要条件を指定できる、長時間のメンテナンスに使用します。
  • Info.plistで識別子を宣言し、起動時にregisterでハンドラーを登録し、earliestBeginDateを設定したリクエストをsubmitします。
  • ハンドラー内で次回の実行を再スケジュールし、expirationHandlerを設定して、必ずsetTaskCompletedを呼び出します。
  • テストでは、LLDBを使ってタスクを強制的に起動します。
無料で開始

AI チューターと学ぶ Swift — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
122
レッスン
409

よくある質問

「バックグラウンドタスクと更新」レッスンは無料ですか?

はい。「バックグラウンドタスクと更新」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「バックグラウンドタスクと更新」で何を学びますか?

BGTaskSchedulerでバックグラウンド処理をスケジュールします。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「バックグラウンドタスクと更新」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. プッシュ通知への登録
  2. 通知ペイロードの処理
  3. バックグラウンドタスクと更新
  4. 通知のアクションとカテゴリ
← Swift Academyに戻る