概要
実行している処理をバックグラウンドでも継続したい場合は
UIApplication.shared.beginBackgroundTask
を使う方法があります。
公式より
https://developer.apple.com/documentation/uikit/uiapplication/1623031-beginbackgroundtask
func beginBackgroundTask(expirationHandler handler: (() -> Void)? = nil) -> UIBackgroundTaskIdentifier
※ここのhandler
はバックグラウンドで実行できる時間が0になる直前で呼び出される。
発生した問題
自分が担当しているアプリではバックグラウンドに移行した時点でタスクを一時停止するため、handler
で実行する処理は何もないと思い、下記のようにしていた。
taskIdentifier = UIApplication.shared.beginBackgroundTask()
ところが、アプリのアップデート後に「処理がいつまで経っても終わらない」、「スリープから復帰するとアプリが最初の画面に戻る」などの声が出るようになった。
色々調べてみるとどうやらこのhandler
でタスクを明示的に終了しないとOS側からアプリが終了させられてしまうらしい。
バックグラウンドで処理が継続できる時間は環境により変わるらしいが、自分が測ったら3分~5分ほど。
現象としては下記のようだった。
- 長い処理があるのでユーザーが端末を放置
- 端末が自動スリープしてバックグラウンドに移行
- バックグラウンドで実行できる時間が0になり、OSからアプリが終了される
- ユーザーがアプリを起動するとスプラッシュの画面に戻っている
記事のタイトルは"アプリが落ちていた"にしたが正確にはOSにより終了させられていた。
ログにもyour app is terminated.
みたいなものが出ていたと思います。たしか。
対応
対応としては下記のようにして明示的にタスクを終了させる。
taskIdentifier = UIApplication.shared.beginBackgroundTask {
UIApplication.shared.endBackgroundTask(self.taskIdentifier)
}
それと、そもそもスリープに入ってしまうほど長い処理があるので処理中のみスリープさせないように対応した。
UIApplication.shared.isIdleTimerDisabled = true
最後に
そもそもhandler
の説明に書いてある。
A handler to be called shortly before the app’s remaining background time reaches 0. Use this handler to clean up and mark the end of the background task. Failure to end the task explicitly will result in the termination of the app. The system calls the handler synchronously on the main thread, blocking the app’s suspension momentarily.
アプリのバックグラウンドの残り時間が0になる直前に呼び出されるハンドラです。このハンドラを使用して、バックグラウンドタスクのクリーンアップと終了をマークします。タスクを明示的に終了させないと、アプリが終了してしまいます。システムはメインスレッド上で同期的にハンドラを呼び出し、アプリのサスペンドを一時的にブロックします。
ちゃんと説明書を読めってことですね。