Advances in App Background Execution(日本語まとめ)

Satsuki Hashiba
9 min readJul 1, 2019

--

本記事は、WWDC2019のセッションである “Advances in App Background Execution” をまとめたものです。

バックグラウンドでの処理を必要とするケースは数多くあります。例えばナビゲーションや音楽再生、ファイルのダウンロードやアップロードなどです。Appleはこれらのバックグラウンド処理を可能にするために、APIを提供しています。

Background Executionとは

アプリにはいくつかの状態があります。

Not runnningはアプリが実行されていない状態です。Foregroundはユーザがアプリを使用している状態で、画面に表示されています。Backgroundはユーザに見えていない状態でコードを実行している状態です。Suspendedではコードを実行していないけれど、アプリはメモリ上に存在します。

バックグラウンド処理とは、アプリがフォアグランドにない状態で、コードを実行することです。アプリがユーザに見えている必要はありません。

アプリがバックグラウンド状態に入る方法は2種類あります。

  • アプリによるリクエスト
    ファイルのダウンロードやアップロードなど
  • イベントトリガー
    位置情報の変更やデータの取得など

また、バックグラウンド処理を行う際、特に注意を払わなければいけないことが3つあります。

  • Power
    適切な量にすることでデバイスのバッテリー消費を抑える
  • Performance
    バックグラウンド処理中、他のアプリに与える影響を最小限にする
  • Privacy
    バックグラウンド処理にユーザは気づかないので、利用しているデータを知らせ、透明化する

バックグラウンド処理のベストプラクティス

様々なバックグラウンド処理が存在しますが、それぞれに適したAPIを使い分けましょう。

Background Task Completion
友人にメッセージを送信した時、ユーザはこの処理がすぐに実行されると考えています。しかしながら、サーバの混雑などが原因で、送信に少し時間がかかってしまう場合があります。この「少しの時間」に、ユーザはアプリを離れたりデバイスをロックしたりしてしまいます。このタスクを完了まで保護する必要しなければなりません。

この時に利用するべきAPIがBackground Task Completionです。これは、アプリが一時停止される前にバックグラウンドで追加の処理を実行します。また、フォアグラウンドで開始された処理を完了するためのものです。以下のAPIを利用して実装します。

VoIP Pushes Notification
VoIPプッシュは特別なプッシュ通知で、アプリが起動されていなくても、即座に立ち上げコードを実行することができます。この際に着信のUIを表示させることで、標準の電話アプリと同じような挙動を実現することができます。WWDC2019より、VoIPプッシュを有効にするためにはCallKitのdidReceiveIncomingPushの実装が必須となりました。

Background Pushes
メッセージアプリにおいてユーザは多くのスレッドを持ちますが、いくつかのスレッドは通知をミュートしたい場合があります。ただ、スレッドを開いた時には新規メッセージが表示されている状態が望ましいでしょう。ユーザには通知したくないが、デバイスには新たなコンテンツの取得を知らせたい場合、Background Pushesを使います。これを利用する場合には、プッシュ通知の“content-available”の値を1にし、“alert”, “sound”, “badge”の値を付与せずに送信します。

バックグラウンドプッシュは受け取った瞬間にアプリが立ち上がる訳ではありません。バッテリー消費やアプリの利用頻度に応じて、コンテンツのダウンロードに適した時間帯をシステムが判断します。

WWDC2019の変更により、バックグラウンドプッシュを送る際には、通知のapns-priority5に、apns-push-typebackgroundに設定する必要があります。apns-push-typeはwatchOSでは必須ですが、他のプラットフォームにおいても設定することを強く推奨されています。

Discretionary Background URL Session
新しいデバイスでログインした時、ユーザは会話のリストや直近のメッセージなどをすぐに必要とするでしょう。しかし、たくさんの古いコンテンツは、すぐにダウンロードする必要はありません。フォアグラウンド状態の時に大量のダウンロードを実行すると、パフォーマンスに影響を与えてしまいます。すぐに必要がないのであれば、夜にデバイスをチャージしている時などに処理を遅延させるのが良いでしょう。

このような場合にDiscretionary Background URL Sessionを利用します。これは、コンテンツのダウンロードを最適なタイミングまで遅延させます。私たちはシステムに対し、タイミングをより賢くスケジューリングするための情報を提供することができます。例えばタイムアウトや、最短でいつタスクを開始するか、どの程度のデータ量を扱うのかなどを指定できます。

新しいBackgroundTasks Framework

データベースとの同期やバックアップなどのメンテナンス作業は、遅延させても良い処理です。また、他のフォアグラウンドの処理に影響を与えたくないので、この作業はデバイスが充電中で使用されていない時に行いたいです。こうした用途に対し、Background Processing Tasksという新しいモードが用意されました。加えて、既存のAPIを改良したBackground App Refresh Tasksも登場します。

Background Processing Tasks

Background Processing Tasksでは数分間の処理を行うことができます。バックアップなどの遅延可能なメンテナンス処理だけでなく、Core MLの学習などにも利用することができます。もしパワーを消費しすぎた場合には、バッテリー寿命を維持するためにアプリが終了させられます。フォアグラウンドで処理が要求された場合や、アプリがよく利用されている場合に、このタスクを実行することができます。

複数のタスクを登録することができますが、一つのタスクに数分間処理できる時間が与えられる訳ではなく、一度の立ち上げに対して処理できる数分間が割り当てられることに注意してください。

Background App Refresh Tasks

Background App Refresh Tasksは新しいAPIで、30秒間の実行を通してアプリを常に最新の状態に維持します。ユーザのアプリ利用パターンに応じてこのタスクの実行頻度が決定されます。例えば朝昼晩にアプリを開く習慣が見られる場合には、その時間帯の少し前にアプリが立ち上げられ、コンテンツを取得することができます。ユーザがアプリをあまり利用しない場合には、このタスクもあまり実行されません。過去のUIApplication fetch APIは非推奨となります。

実装

まず、Targetにて利用するBackground modeをオンにします。Background App Refresh TasksはBackground fetchに、Background Processing TasksはBackground processingにチェックを入れましょう。その後、info.plistに Permitted background task scheduler identifiersにタスクのidentifierを登録します(β版のスクリーンショットは載せられないため、詳しくはセッション動画のdemoをご参照ください)。

タスクの処理を実装し、BGTaskSchedulerに登録します。

同一identifierでインスタンスを作り、スケジューリングのための情報を提供します。BGTaskSchedulerにsubmitすることで知らせてあげます。

以上でBackground Tasksを実装できます。Background App Refresh Tasksは一日中アプリを最新の状態に保ちたいので、タスクの処理の中で再びタスクの登録を行うようにしましょう。

デバッグ方法

BackgroundTasksがきちんと動いているかどうかを確認する方法も用意されています。requestをsubmitした後にプログラムを止め、コンソールから以下のコマンドを実行することで、BackgroundTasksを発火・停止させることができます。

// 発火
(lldb) e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"TASK_IDENTIFIER"]
// 停止
(lldb) e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"TASK_IDENTIFIER"]

備考

earliestBeginDateはあまり遠い日時に設定すべきではありません。例えば1ヶ月後に設定した場合、ユーザが1ヶ月間アプリを開かなかった時に登録されたBackgroundTaskが突然発火します。この挙動はユーザが予測できません。この値は1週間以内で指定すべきです。

WWDC2019で発表された新たなBackgroundTasks Frameworkは、アプリのユーザ体験を大きく向上させることができます。Power, Performance, Privacyの3点に注意して、適切なバックグラウンド処理を実装しましょう。

参考文献

--

--