LoginSignup
114
112

More than 5 years have passed since last update.

iOS7バックグラウンド通信(NSURLSession)のライフサイクルまとめ

Last updated at Posted at 2014-07-27

iOS7でBackground Transferを使うときのライフサイクルが分かりにくいので実際に試してみました。アプリの生死に関わらず通信が継続されるというのは本当なのか?通信成功の処理と通信失敗の処理はどこに実装すればいいのか?をはっきりさせます。

iPhone5実機(iOS7.1.2)にて以下のデモアプリを使用してコールバックを確認しました。このデモアプリは複数ファイルダウンロード時の挙動が分かりやすくて良いです。

Background Transfer Service in iOS 7 SDK: How To Download File in Background

アプリフォアグラウンドでダウンロード(1セッション2タスク)したとき

複数のファイルをダウンロードしているとき、アプリがフォアグラウンドであればリアルタイムで次のメソッドが実行されます。
ファイルのダウンロード順は保証されません。

  • (ファイル1)ダウンロード進捗時 URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: (NSURLSessionDownloadDelegate)
  • (ファイル1)ダウンロード完了時 URLSession:downloadTask:didFinishDownloadingToURL: (NSURLSessionDownloadDelegate)
  • (ファイル1)ダウンロード完了後 URLSession:task:didCompleteWithError: (NSURLSessionTaskDelegate)
  • (ファイル2)ダウンロード進捗時 URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: (NSURLSessionDownloadDelegate)
  • (ファイル2)ダウンロード完了時 URLSession:downloadTask:didFinishDownloadingToURL: (NSURLSessionDownloadDelegate)
  • (ファイル2)ダウンロード完了後 URLSession:task:didCompleteWithError: (NSURLSessionTaskDelegate)

全タスクが終了したときのコールバックは無いようです。
すべてのタスクが完了し、セッションが不要になったらfinishTasksAndInvalidate (NSURLSession)を実行してセッションを破棄します。

アプリバックグラウンドまたはプロセスが死んでいる状態でダウンロード(1セッション2タスク)したとき

アプリがクラッシュしたとき、OSの判断でアプリプロセスが殺されたとき、アプリがバックグラウンドにあるときは同じライフサイクルとなります。
ファイルのダウンロード順は保証されず、すべてのファイルダウンロードが完了したときに以下のコールバックが順番に、まとめて実行されます。また、進捗コールバックも実行されません。

  • バックグラウンドでセッションが完了した application:handleEventsForBackgroundURLSession:completionHandler: (UIApplicationDelegate)
  • (ファイル1)ダウンロード完了 URLSession:downloadTask:didFinishDownloadingToURL: (NSURLSessionDownloadDelegate)
  • (ファイル1)ダウンロード完了 URLSession:task:didCompleteWithError: (NSURLSessionTaskDelegate)
  • (ファイル2)ダウンロード完了 URLSession:downloadTask:didFinishDownloadingToURL: (NSURLSessionDownloadDelegate)
  • (ファイル2)ダウンロード完了 URLSession:task:didCompleteWithError: (NSURLSessionTaskDelegate)
  • URLSessionDidFinishEventsForBackgroundURLSession: (NSURLSessionDelegate)

バックグラウンドダウンロードのライフサイクルではhandleEventsForBackgroundURLSessionからURLSessionDidFinishEventsForBackgroundURLSessionまでを30秒以内に完了する必要があります。handleEventsForBackgroundURLSessionで受け取ったcompletionHandlerURLSessionDidFinishEventsForBackgroundURLSessionで実行します。30秒経過してもcompletionHandlerが呼ばれていなければ、プロセスは強制終了されます。

この一連の流れはセッションごとに実行されるはずなので、複数のセッションを同時に実行している場合にはcompletionHandlerをセッションごとに保持できるように配慮が必要です。

すべてのタスクが完了し、セッションが不要になったらfinishTasksAndInvalidate (NSURLSession)でセッションを破棄します。

アプリバックグラウンドでダウンロード実行中にユーザの操作でアプリが終了したとき

ユーザがアプリ切り替えからスワイプ操作でアプリを終了すると、バックグラウンドダウンロードも強制的に切断され、エラー扱いとなります。
NSURLSessionのコールバックも一切呼ばれることなく終了してしまいますが、ユーザ操作によるアプリを再起動時にエラーを検出することは可能です。

  • アプリがバックグラウンドかつダウンロードが進行中の状態で、ユーザ操作によってアプリが終了した
    • -> コールバックは一切実行されない。ダウンロードキャンセルされる。
  • ユーザ操作によってアプリが再起動した
    • -> URLSession:task:didCompleteWithError: (NSURLSessionTaskDelegate) がエラー付きで実行される。
    • エラー内容は「The operation couldn’t be completed. (NSURLErrorDomain error -999.)」 (エラーコード-999はNSURLErrorCancelled)
    • セッションIDからセッションを復元することで初めてエラーハンドリングが可能

この場合、OSからセッションIDを受け取ることはできないため、アプリ側でセッションIDを永続化しておく必要があります。

まとめ

フォアグラウンドダウンロード、バックグラウンドダウンロード、エラー発生時のすべての場合でdidCompleteWithErrorは実行されています。didCompleteWithErrorに成功時と失敗時のロジックを実装しておくのが一番素直だと思います。

また、セッションのinvalidate処理もdidCompleteWithErrorに実装することができれば安全です。

ということで、以下の結論となりました。

  • アプリの生死に関わらず通信が継続されるというのは本当なのか?
    • -> ユーザ操作によるアプリ終了時のみ、通信は中断される。それ以外は基本的に通信継続。ただし、アプリバックグラウンド状態でダウンロード進捗を受け取ることはできない。
  • 通信成功の処理と通信失敗の処理はどこに実装すればいいのか?
    • -> とりあえずdidCompleteWithErrorで良い。セッションの復元やセッションのinvalidate処理も忘れずに。

参考

114
112
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
114
112