Xcode
iOS
background
バックグラウンド実行
swift4

[Swift] iOSのバックグラウンド処理について

バックグラウンド処理についていろいろ調べる機会があったのでまとめます。

実行環境

  • macOS High Sierra
  • Swift 4
  • iOS 11

長時間のタスク実行

iOSのバックグラウンドタスク実行には長時間のものと短時間(概ね180秒以内)のものがあります。

iOSでは一部の例外を除き基本的に長時間のバックグラウンド(常駐)処理が許されていません。バッテリを始めとしたリソース管理の観点からだと思われます。
長時間の処理が許されるのは以下のタスクです。

  • 音の再生・AirPlay(音楽を再生するなど)
  • 位置情報更新(カーナビアプリはアプリを閉じてもナビし続けますね。)
  • 通話
  • Newsstand向けダウンロード
  • 外部アクセサリとの通信
  • Bluetoothアクセサリとの通信
  • background fetch
  • 通知(Notification)の監視

各々のトピックについては他の記事をあたってください。もう少し詳しいことはこちらに載ってます。(Appleの英語リファレンスですが。)

短時間のタスク実行

今回はこちらが本題です。iOSの場合いわゆる常駐処理はできることが限られていますが短時間のタスク実行なら比較的なんでもできます。
Appは通常ユーザーがその画面を閉じたらすぐに処理が止まりますが以下に示すようなコードで画面を閉じられてもある程度処理を続行できます。時間のかかるダウンロード/アップロード処理を継続するのに使うといいですね。

ViewController.swift
class ViewController: UIViewController {
  // backgroundTaskIDの初期値はなんでも良さそうです。とりあえず0を代入しておきます。
  var backgroundTaskID : UIBackgroundTaskIdentifier = 0
  @IBOutlet weak var button:UIButton!

  override func viewDidLoad() {
    super.viewDidLoad()
  }

  // ボタンが押されたら時間のかかる処理をする。
  // 処理中にユーザがアプリを閉じても大丈夫なようにしたい。
  @IBAction func onButtonTapped(_ sender: UIButton) {
    // 中断されたら困る処理の起点にコレを書く
    self.backgroundTaskID = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
    self.longTimeFunction()
  }

  func longTimeFunction() {
    // 長い時間かかる処理
    // 処理が終わったらコレを書く
    UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
  }
}

UIApplication.shared.beginBackgroundTask() が呼ばれるとアプリがバックになってもUIApplication.shared.endBackgroundTask()が呼ばれるまでiOSは処理を継続します。一説によれば処理の継続限度は180秒とのことですが、状況によりけりだそうです。
beginBackgroundTask()はコードにあるようにexpirationHandlerを渡すことができます。expirationHandlerはバックグラウンドの処理時間の期限が迫ると呼ばれるとのことです。「お前もうすぐ死ぬから最後の始末しておけ」ハンドラですね。

まとめ

iOSでバックグラウンド処理をする方法についてまとめてみました。beginBackgroundTask()はバックグラウンド処理を実行するというよりもアプリがactiveの状態で実行中の処理がユーザのアプリ切り替えによって中断されないようにする感じですね。
一般に言われるバックグラウンド/常駐プロセスは用途が制限されています。が、その用途に当てはまるように見せかけることができれば使えてしまうわけですね。だいぶ古いネタではありますがFacebookが裏で無音を流してバックグラウンド処理をしていたというのは有名な話です。

それでは皆さん良いswiftライフを!