Universal Linksの実装が落ち着き、次はアプリを開いていない間にもデータを最新化しておきたくなり、BGTaskSchedulerでのバックグラウンド更新処理に挑戦しました。
1. 指定した時間に必ず実行されると思い込んでいた
最初、BGAppRefreshTaskRequestに実行したい時刻を指定すれば、その時刻ぴったりに処理が走るものだと思い込んでいました。
let request = BGAppRefreshTaskRequest(identifier: "com.example.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 15)
実際にはearliestBeginDateは「これより早くは実行しない」という下限を示すだけで、実際の実行タイミングはOSが端末のバッテリー状況や利用状況を見て判断します。指定した時刻通りに動くとは限らないという前提で設計し直す必要がありました。
2. Info.plistへの識別子登録を忘れていた
タスクの登録処理を書いたのに、いつまでもタスクがスケジュールされず原因が分からず悩みました。
Info.plist:
BGTaskSchedulerPermittedIdentifiers に
com.example.refresh を追加
コード側で使っているタスク識別子を、Info.plistのBGTaskSchedulerPermittedIdentifiersにも登録しておく必要がありました。片方だけ書いても動かず、両方を一致させて初めて機能する仕組みでした。
3. 実機デバッグ用のコマンドを知らず時間を無駄にした
バックグラウンドタスクの動作確認のため、何時間も端末を放置して自然発火を待とうとして時間を無駄にしていました。
デバッガーの一時停止中に実行:
e -l objc -- (void)[[BGTaskScheduler sharedScheduler]
_simulateLaunchForTaskWithIdentifier:@"com.example.refresh"]
Xcodeのデバッガーコンソールでこのコマンドを実行すると、実機上でタスクの発火を強制的にシミュレートできると知り、以降の動作確認が一気に楽になりました。
4. タスク内で時間のかかる処理をしてタイムアウトした
バックグラウンドタスク内で通信処理を書いたところ、途中で処理が打ち切られてデータが中途半端な状態になることがありました。
バックグラウンドタスクにはOSから与えられる実行時間に制限があり、想定より短く打ち切られることがあります。expirationHandlerを設定して、時間切れが近づいたら処理を安全に中断する仕組みを用意していなかったのが原因でした。中断時の後処理を実装し、次回起動時に未完了分を再開できるようにして解決しました。
まとめ
earliestBeginDateは実行の下限時刻を示すだけで、正確なタイミングでの実行は保証されない- タスク識別子はコードと
Info.plistのBGTaskSchedulerPermittedIdentifiersの両方に登録する必要がある - 実機での動作確認は、デバッガーコンソールからの強制発火コマンドを使うと効率的
- タスクの実行時間には制限がある。
expirationHandlerで中断に備えた設計にする
「バックグラウンドで動かすだけ」の単純な話ではなく、OSに実行タイミングを委ねる前提での設計変更が必要な実装でした。
次は、MapKitで地図表示を実装してつまずいた話をまとめます。
本記事は生成AI(Claude)を活用して執筆・編集しています。内容は実体験に基づき、執筆者本人が確認・監修しています。


コメント