この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
iOS 11では、NSURLSessionに、接続状態を監視する機能が組み込まれました。
これまでは、NSURLSessionでアクセスを開始した時、ネットワークに問題があると、直ちにエラーとなっていました。 そして、ネットワークの問題が解消された後に、アクセスを再開する場合、改めてセッションを開始する必要がありました。
また、「接続が確保されたら、直ちにアクセスしたい」と言うような要件では、SCNetworkReachabilityなどを使用したポーリングの実装が必要でした。
iOS 11以降では、このような接続監視の実装が、まったく必要なくなったと言えます。
本記事は Apple からベータ版として公開されているドキュメントを情報源としています。 そのため、正式版と異なる情報になる可能性があります。ご留意の上、お読みください。
2 動作確認
次のようなサンプルで動作を確認してみます。
requestCachePolicyは、.reloadIgnoringLocalCacheDataに設定し、動作が確認しやすいように、キャッシュを無効にしました。
class ViewController: UIViewController {
// ・・・略・・・
@IBAction func tapConnectButton(_ sender: Any) {
let config = URLSessionConfiguration.default
config.requestCachePolicy = .reloadIgnoringLocalCacheData // キャッシュ無効にする
config.waitsForConnectivity = false // デフォルト値
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
let url = URL(string:"https://classmethod.jp/")!
let task = session.dataTask(with: url) { (data, response, error) in
if let error = error {
print("ERROR \(error)")
print(error)
} else {
print("SUCCESS statusCode : \((response as! HTTPURLResponse).statusCode)")
}
}
task.resume()
}
}
extension ViewController: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
print("URLSession:taskIsWaitingForConnectivity:")
}
}
(1) 従来の動作
当初、新しく追加されたプロパティであるwaitsForConnectivityを、false(デフォルト値)に設定し、今までと同じで動作を見ておきます。
インターネットにアクセスを開始すると、接続状態に問題がない場合、error = nil でdataTask の completionHandler:ハンドラに制御が移ります。
Log> SUCCESS statusCode : 200
そして、接続状態に問題がある状況を作為するため、機内モードをONにして、実行してみると、error != nil で、やはり直ちにcompletionHandler:ハンドラに制御は返ります。
Log> ERROR Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline."
(2) 接続状態の監視
続いて、waitsForConnectivityを、trueに設定し、同様に動作を確認してみます。
接続状態に問題がない場合、error = nil でdataTask の completionHandler:ハンドラに制御が戻ります。これは、上の動作と同じです。
Log> SUCCESS statusCode : 200
そして、機内モードをONにして、実行してみると、completionHandler:ハンドラへは帰らず、dataTaskがブロックしたようになります。
そして、この時、これも新設されたデリゲートメソッドである、urlSession(_:taskIsWaitingForConnectivity:)が呼ばれます。
Log> URLSession:taskIsWaitingForConnectivity:
続いて、このブロックされた状態のままで、機内モードをOFF(インターネット接続を復活)にすると、error = nil でdataTask の completionHandler:ハンドラに制御が戻ります。
SUCCESS statusCode : 200
3 接続不良な状態
先の動作確認では、機内モードをONにすることで、接続不良の状態を試しましたが、WWDC2017のセッションの中では、この他に、Wi-Fiネットワークが繋がっていないときや、電波な受信できないときなどが、列挙されていました。
色々な要因でインタネット接続が確保できない時、この監視が有効になるようです。
WWDC2017 Advances in Networking より
4 waitsForConnectivity
このように、waitsForConnectivityをtrueに設定するだけで、URLSessionはネットワークの状態を監視し、タスクの開始を待機するようになります。
なお、バックグラウンドで動作するURLSessionでは、自動的に有効になっているとのことです。
5 URLSession:taskIsWaitingForConnectivity:
URLSessionTaskDelegateに新しく追加された、URLSession:taskIsWaitingForConnectivity:は、接続監視の待機に入った時、1回だけ呼ばれます。(待機に入らなかった場合は、呼ばれません)
URLSessionTaskDelegate urlSession(_:taskIsWaitingForConnectivity:)
6 最後に
waitsForConnectivityを使用することで、ポーリングの実装や、重複したNSURLSessionの接続を記述する必要はなくなると思います。
ただし、これを利用する場合、URLSessionTaskDelegateを適切に処理することは、新しくアプリ開発者の仕事であると言えそうです。
7 参考リンク
Advances in Networking, Part 2
NSURLSession
NSURLSessionTaskDelegate
urlSession(_:taskIsWaitingForConnectivity:)
taskIsWaitingForConnectivity: