はじめに
WWDC2019で行われた下記のセッションをベースに、実際に実装してみた結果をまとめます。
https://developer.apple.com/videos/play/wwdc2019/707/
全体感
以下の図に記載しました。
この中の 処理種別
に関して、ナンバリングしてあるものがiOSで利用できるバックグラウンド処理です。
※ Audio, LocationUpdate, VoIP, Bluetoothなどに関しては今回は省いています
各処理の比較
処理種別 | 実行可能時間 | 実行可能状態 | 詳細 |
---|---|---|---|
1. Background Task Completion | 環境依存(※1) | フォアグラウンドで実行した直後のみ | https://qiita.com/chocoyama/items/8162bf67e452e41b574b |
2. Background Notification (Silent Push) | 30秒間 | ユーザーによって強制終了されていない状態 | https://qiita.com/chocoyama/items/56cd3ac2daaf69dffa0f |
3-1. Background URL Session | 環境依存(※1) | 〃 | https://qiita.com/chocoyama/items/1440b2e3d69856ba8957 |
3-2. Discretionary Background URL Session | 〃 | 〃 | https://qiita.com/chocoyama/items/1440b2e3d69856ba8957 |
4-1. BackgroundTasks (App Refresh Tasks) | 30秒間 | 〃 | https://qiita.com/chocoyama/items/d69322932f400a5d012b |
4-2. BackgroundTasks (Processing Tasks) | 数分間 (※2) |
〃 | https://qiita.com/chocoyama/items/d69322932f400a5d012b |
※1. 環境依存
と記述している箇所はシステムの状態やOSバージョンによって異なるようです。
たとえば、iOS12以前だと180secほど実行できていたバックグラウンド処理が、iOS13では30secまで縮められていました。
※2. 以下のような表記がされているだけで、最長の実行時間については明記がありませんでした。
A request to launch your app in the background to execute a processing task that can take minutes to complete.
https://developer.apple.com/documentation/backgroundtasks/bgprocessingtaskrequest
個人的な感想
全体として
バックグラウンド処理は多くの場合以下のような特徴があるため、フォアグラウンド処理と同等に扱うことはできません。
- 実行タイミングがシステムに依存している
- 処理時間が制限されている
- 実行時点での環境が様々(バッテリー状況、通信状況など)
- etc...
「必ず実行されるもの」という前提で実装を組んでしまうと、想定した動作にならないことが容易に起きます。
またVoIPプッシュなどの一部の処理を除いて、ユーザーによって強制的にアプリが終了されている場合は、バックグラウンド処理は起動されません。
そのため、これらの処理は基本的に「実行されるとより便利になるもの」といった位置付けで実装するのが現実的だと思います。
1. Background Task Completion
詳細 → https://qiita.com/chocoyama/items/8162bf67e452e41b574b
「処理の途中でアプリをバックグラウンドにされてしまった時の対処」が必要なケースで使えるものです。
WWDCの動画では、メッセージ送信処理を例にしていました。
※ この機能を使って無限にバックグラウンド処理を実行させようという試みを、調査の中で見つけることが出来ました。
バックグラウンド処理の完了後に、再度バックグラウンド処理を開始させる方法です。
実際に掲載されているサンプルコードを試してみましたが、バックグラウンドでの実行可能時間は特に延長されず、期待した動作にはなりませんでした。
そもそも、無限にバックグラウンド処理を行うのはiOSが想定している本来の使い方と異なるため、避けた方が良い気がします。
ユーザーとしても、知らない間にアプリが無限にバックグラウンド処理を行っているのは迷惑な挙動になりうるでしょう。
2. Background Notification (Silent Push)
詳細 → https://qiita.com/chocoyama/items/56cd3ac2daaf69dffa0f
サーバーサイドから、端末に対して特定の処理を動作させることができる貴重な手段の一つです。
ただし、配信の保証ができないことに加えて、即時で実行されないことも多いので、この仕組みに依存した実装を行うのは危険です。
あくまでオプショナルな機能として、より便利にアプリを使えるためのものとして考えておいた方が良さそうです。
例えば、サーバーサイドで何らかのデータ更新が走った際に、Background Notification
を利用してアプリ側に同期を取ろうとする場合を考えます。
通知がうまく配信された場合、「ユーザーがアプリを起動したタイミングではすでに最新のデータが取れている」といった、より良い体験を与えることができます。
しかし、そうでない場合は起動時に最新のデータを取得する必要があるので、結局Background Notification
だけでは十分な実装にはならないでしょう。
必須で行わなけらばならない処理は実装しておきつつ、さらに便利にするために追加で実装する、といった使い方になるような気がします。
また、フォアグランド時にBackground Notification
を受けた場合に限っては、必ずそれをハンドリングできるようです。
この特徴を利用して、「フォアグラウンド時に、画面上には通知を出さずに裏でリアルタイム同期を行う」といった用途にも使えそうです。
3-1. Background URL Session
詳細 → https://qiita.com/chocoyama/items/1440b2e3d69856ba8957
これを使うと、バックグラウンドになったタイミングでもフォアグラウンドで開始した通信を継続させることができます。
Background Task Completion
でも同じようなことは実現できるため、自分は用途があまり思い浮かびませんでした。
3-2. Discretionary Background URL Session
詳細 → https://qiita.com/chocoyama/items/1440b2e3d69856ba8957
アプリがフォアグラウンドの時に、何らかのイベントに応じて通信の予約をしておける機能です。
WWDCの動画では、「ユーザーがサインインした直後、古いコンテンツをパフォーマンスに影響しないタイミングで取得する」といったユースケースを例にしていました。
端末にデータを溜めないようなアプリではあまり使うことがなさそうですが、必要に応じて使うと良さそうです。
4-1. BackgroundTasks (App Refresh Tasks)
詳細 → https://qiita.com/chocoyama/items/d69322932f400a5d012b
ユーザーがよく使うアプリについて、起動を先回りしてデータを取得しておくことができます。
これを利用して細かいアップデートを事前に行っておくことができますが、実行時間は30秒しかないため、重たい処理を行いたい場合はProcessing Tasks
を使うことになるでしょう。
基本的にはDBやUserDefaultsなどにデータを書き込むようなユースケースになりそうです。
オンメモリのデータを更新する用途でも使えなくはなさそうですが、タスク実行からアプリ起動までに時間が空いてしまうと、システムによって終了させられる可能性もあるため、あまり有効ではないかなという気はしました。
必ずしも実行される処理ではないので、ここでの処理が行われなくてもフォアグラウンド時に埋め合わせを行えるような実装にしておくことが求められると思います。
4-2. BackgroundTasks (Processing Tasks)
詳細 → https://qiita.com/chocoyama/items/d69322932f400a5d012b
定期的に行いたい処理で、 App Refresh Tasks
では対応できない重たい処理を行いたい場合はこれを使うことになります。
CPUMonitorをOFFにできるなどの強力な機能もついているので、この機能で実現できない処理はあまりないように思います。
ただ一般的なバッチ処理とは異なり、「正確な日時を指定することができない」「端末が指定した実行条件に置かれないと実行されない」といった特徴を持っているので、確実に毎日実行させられるとは限らないことは考慮した方が良さそうです。