[iOS 11]NSURLSessionの新しいプロパティ(waitsForConnectivity)で接続監視が可能になりました #WWDC17

WWDC2017

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 = nildataTaskcompletionHandler:ハンドラに制御が移ります。

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 = nildataTaskcompletionHandler:ハンドラに制御が戻ります。これは、上の動作と同じです。

Log> SUCCESS statusCode : 200

そして、機内モードONにして、実行してみると、completionHandler:ハンドラへは帰らず、dataTaskがブロックしたようになります。

そして、この時、これも新設されたデリゲートメソッドである、urlSession(_:taskIsWaitingForConnectivity:)が呼ばれます。

Log> URLSession:taskIsWaitingForConnectivity:

続いて、このブロックされた状態のままで、機内モードOFF(インターネット接続を復活)にすると、error = nildataTaskcompletionHandler:ハンドラに制御が戻ります。

SUCCESS statusCode : 200

3 接続不良な状態

先の動作確認では、機内モードをONにすることで、接続不良の状態を試しましたが、WWDC2017のセッションの中では、この他に、Wi-Fiネットワークが繋がっていないときや、電波な受信できないときなどが、列挙されていました。

色々な要因でインタネット接続が確保できない時、この監視が有効になるようです。

001
WWDC2017 Advances in Networking より

4 waitsForConnectivity

このように、waitsForConnectivitytrueに設定するだけで、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: