[iOS 10] NSURLSession の新機能について

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

New Features and Best Practices

WWDC 2016 で発表された NSURLSession に関する以下の新機能についてご紹介します。

  • HTTP/2 Server Push
  • Network statistics
  • Security

NSURLSession Example

iOS で通信周りを実装する場合は NSURLSession を利用することが推奨されています。
(以前まで利用できた NSURLConnection は deprecated となっています)
まずはこのクラスを利用した通信の簡単なサンプルをご紹介します。

3 つのステップ

  1. Configuration オブジェクトの生成
  2. Session の生成
  3. Tasks の生成
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "https://www.example.com/")!

let task = session.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in
    // do something
}

task.resume()

これが一番簡単な通信の実装です。
デフォルトの Configuration を生成し、通信を行っています。

NSURLSessionConfiguration では以下の項目を設定可能です。

  • TLS バージョン
  • Wi-Fi アクセス時のみ通信許可
  • ネットワークサービスタイプ
    • VoIP, Video, Voice など
  • Cookie ポリシー
  • Cache ポリシー
  • ストレージオブジェクト
  • リクエストとリソースのタイムアウト

HTTP/2 Server Push

iOS 10 からは NSURLSession で HTTP/2 がサポートされました。
これにより HTTP/2 の Server Push という機能が利用できるため、パフォーマンスが向上しています。

これはサーバーが Server Push をサポートしている必要がありますが、iOS 側は特に何もする必要はありません。
NSURLSession をいつも通りに使うだけで、その恩恵を受けることができます。

デモでは HTTP/2 Server Push を利用した時とそうでない時で画像の読み込み速度を比較していましたが、Server Push を利用している時では利用していない時に比べ 2.5 倍 ほどの速度が出ていました。

Network statistics

iOS 10 からは通信周りがかなりデバッグしやすくなったようです。
NSURLSessionTaskMetrics というクラスが追加され、通信に関する様々な情報を確認することができるようになりました。

プロパティ

  • taskInterval: NSDateInterval
    • タスクがインスタンス化されてから完了するまでの時間を返す
  • redirectCount: Int
    • タスクの実行中にリダイレクトされた回数を返す
  • transactionMetrics: [NSURLSessionTaskTransactionMetrics]
    • NSURLSessionTaskTransactionMetrics クラスの配列を返す
    • 中身は個別の request/response トランザクションデータ

以下のデリゲートメソッドを実装することで、データが取得できたタイミングでその内容を確認できます。

URLSession:task:didFinishCollectingMetrics:

NSURLSessionTaskTransactionMetrics の詳細

NSURLSessionTaskTransactionMetrics のプロパティは 4 つのカテゴリに分類されます。

1. リクエストとレスポンス

  • request: NSURLRequest
  • response: NSURLResponse?

2. プロトコルとコネクション

  • networkProtocolName: String?
  • isProxyConnection: Bool
  • isReusedConnection: Bool

networkProtocolName には以下の様な文字列が入るそうです。

  • http/1.1
  • h2
  • spdy/3, spdy/3.1

3. 読み込み情報

  • resourceFetchType: NSURLSessionTaskMetricsResourceFetchType
    • .networkLoad
      • ネットワークからの読み込み
    • .localCache
      • ローカルキャッシュからの読み込み
    • .serverPush
      • Server Push からの読み込み

4. コネクションの確立と送信

  • fetchStartDate: NSDate?
  • domainLookupStartDate: NSDate?
  • domainLookupEndDate: NSDate?
  • connectStartDate: NSDate?
  • secureConnectionStartDate: NSDate?
  • secureConnectionEndDate: NSDate?
  • connectEndDate: NSDate?
  • requestStartDate: NSDate?
  • requestEndDate: NSDate?
  • responseStartDate: NSDate?
  • responseEndDate: NSDate?

ある処理の開始と終了の日時です。
大枠は以下の画像をご覧ください。

1

サンプルコード

class ViewController: UIViewController, URLSessionTaskDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
        let url = URL(string: "https://www.google.com/complete/search?hl=en&q=a&output=toolbar")!

        let task = session.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in
            // do something
        }

        task.resume()
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        print("redirectCount =", metrics.redirectCount)
        print("taskInterval =", metrics.taskInterval)
        print("transactionMetrics =", metrics.transactionMetrics)
    }
}
実行結果
redirectCount = 0
taskInterval = 2016-09-13 18:10:20 +0000 to 2016-09-13 18:10:21 +0000
transactionMetrics = [(Request) <NSURLRequest: 0x608000014f20> { URL: https://www.google.com/complete/search?hl=en&q=a&output=toolbar }
(Response) <NSHTTPURLResponse: 0x60800002eaa0> { URL: https://www.google.com/complete/search?hl=en&q=a&output=toolbar } { status code: 200, headers {
    "Cache-Control" = "private, max-age=3600";
    "Content-Encoding" = gzip;
    "Content-Length" = 171;
    "Content-Type" = "text/xml; charset=ISO-8859-1";
    Date = "Tue, 13 Sep 2016 18:10:21 GMT";
    Expires = "Tue, 13 Sep 2016 18:10:21 GMT";
    Server = gws;
    "Set-Cookie" = "NID=86=Ktknn9h_W3GvyGnN8jnavS1V-2Roz2103kpLqVEbesM46ZW9LtvWlj_NQU-2Owr4-iiqXv1WZSwVeGNBaMqtNY_buVVCOBC_O-QuiGNVd601l8HlsMs2ZI9KaiosqW82; expires=Wed, 15-Mar-2017 18:10:21 GMT; path=/; domain=.google.com; HttpOnly";
    "alt-svc" = "quic=\":443\"; ma=2592000; v=\"36,35,34,33,32\"";
    p3p = "CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\"";
    "x-frame-options" = SAMEORIGIN;
    "x-xss-protection" = "1; mode=block";
} }
(Fetch Start) 2016-09-13 18:10:20 +0000
(Domain Lookup Start) 2016-09-13 18:10:20 +0000
(Domain Lookup End) 2016-09-13 18:10:21 +0000
(Connect Start) 2016-09-13 18:10:21 +0000
(Secure Connection Start) 2016-09-13 18:10:21 +0000
(Secure Connection End) 2016-09-13 18:10:21 +0000
(Connect End) 2016-09-13 18:10:21 +0000
(Request Start) 2016-09-13 18:10:21 +0000
(Request End) 2016-09-13 18:10:21 +0000
(Response Start) 2016-09-13 18:10:21 +0000
(Response End) 2016-09-13 18:10:21 +0000
(Protocol Name) h2
(Proxy Connection) NO
(Reused Connection) NO
(Fetch Type) Network Load
]

これらを利用すれば解析がしやすくなり、どこで時間がかかっているかがすぐに分かるようになるでしょう。

Security

Transport Layer Security (TLS)

  • 近いうちに RC4 暗号がサポートされなくなる

App Transport Security (ATS)

以下の 2 つのキーが追加されました。

  • NSAllowsArbitraryLoadsInWebContent
    • WKWebView であれば、表示するコンテンツが http 通信でも許可される
  • NSRequiresCertificateTransparency
    • 証明書の透明性が必須

NSAllowsArbitraryLoadsInWebContent は嬉しいですね。
2016 年の年末から ATS 対応が必須となりますが、API の通信まわりで https を使用していれば WebView 内のコンテンツは http でも許容されるようです。
詳しくは こちらの記事 をご覧ください。

最後に

HTTP/2 が今後主流になっていくことで、iPhone での通信もさらに高速化されそうです。
また、通信周りの解析方法も強化されたことで iOS アプリの開発もスムーズになることでしょう。

リンク

ミレニアム・ファルコン製作日記 #35

35 号 表紙

mfd_35_1

パーツ

mfd_35_2

mfd_35_3

mfd_35_4

成果

mfd_35_5

今回の作業は以下の 1 つでした。

  • 下部外殻フレームを組み立てる

第 33 号に続いて、下部外殻フレームの骨組みに当たる搭乗ランプの構造を支える外側部分を組み立てました。
今後の数号で同様の作業を行い、これらのフレームをこれまで組み立てたフレームに追加すると、円形の下部外殻フレームが完成します。

フレーム組み立て作業は何度やっても楽しいです。
下部フレームが完成した後は上部フレームの組み立て作業もあるのでしょうか?
楽しみにしています。

それではまた次回。

May the Force be with you!