[iOS] iOS 9からNSURLSessionのNSURLSessionConfigurationの扱い方が変わっている話

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

はじめに

こんにちは! 加藤潤です。
今日はNSURLSessionで使用するNSURLSessionConfigurationに関するネタです。

NSURLSessionとは

iOS 7から登場した、それまで使用していたNSURLConnectionに取って代わる通信処理をを担うクラスです。 皆さんhttp(s)で使用しているのではないでしょうか。

※ ↓NSURLConnectionはiOS 9.0でDeprecatedになり、代わりにNSURLSessionを使うことが推奨されています。
What's New in iOS 9.0

NSURLSessionConfigurationとは

こちらもiOS 7からNSURLSessionと一緒に登場したクラスです。 タイムアウトの設定やキャッシュポリシー、HTTPヘッダの設定などを行うことができます。

NSURLSessionConfigurationは以下のようにNSURLSessionのイニシャライザーに渡して使用します。

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration)

初期化した後もプロパティを通じてNSURLSessionConfigurationにアクセスできるが...

NSURLSessionを初期化した後でも以下のようにNSURLSessionConfigurationを取得できます。

let config = session.configuration

もちろんパラメータの設定も可能です。 以下はUser-Agentを設定している例です。

config.HTTPAdditionalHeaders?["User-Agent"] = "hogehoge"

しかし、ここで1つ注意しなくてはいけないことがあります。 User-Agentを設定する例を示しましたが、実はこの設定はiOS 9から効かなくなりました。 理由はNSURLSessionのクラスリファレンスでconfigurationプロパティの説明に書かれていました。

NOTE

On iOS 9 and OS X 10.11, NSURLSession objects store a copy of the NSURLSessionConfiguration object passed to their initializers, such that a session’s configuration is immutable after initialization. Any further changes to mutable properties on the configuration object passed to a session’s initializer or the value returned from a session’s configuration property do not affect the behavior of that session.

On previous versions of iOS and OS X, there is a bug in the implementation, whereby NSURLSession objects instead store a reference to configuration objects passed to their initializers. This allows the behavior of a session to be further configured after initialization by modifying the configuration object passed to a session’s initializer or the value returned from a session’s configuration property. You can ensure consistent behavior across different platform versions by explicitly calling copy on configuration objects passed to an NSURLSession initializer or returned from the configuration property.

日本語でざっくり説明すると大体こんな感じでしょうか。

  • iOS 9ではNSURLSessionはイニシャライザーで渡されたNSURLSessionConfigurationのコピーを保持するため、初期化後にconfigurationプロパティの設定を変更してもNSURLSessionの振る舞いには影響しない。
  • iOS 9未満では実装にバグがあり、それによってNSURLSessionはイニシャライザーで渡されたNSURLSessionConfigurationの参照を保持する。それによって初期化後にconfigurationの設定を変更するとNSURLSessionの振る舞いに影響する。

ということで iOS 9からNSURLSessionの初期化後にconfigurationの設定を変更しても実際の通信には反映されなくなりました。

Alamofireを使った実装例

SwiftのネットワーキングライブラリAlamofireでもNSURLSessionNSURLSessionConfigurationは使われているので 上記の挙動の違いに注意して実装する必要があります。

参考までにカスタムのUser-Agentを設定する例を載せておきます。 Alamofireのバージョンは3.3.1で確認しています。

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
// カスタムUser-Agentを設定
configuration.HTTPAdditionalHeaders?["User-Agent"] = "hogehoge"
let manager = Manager(configuration: configuration)

Managerを生成する前にconfigurationの設定を全て行う必要があります。

まとめ

iOS 9がリリースされてからもうだいぶ経ちますが、意外と知らない人もいるのではないかと思い記事にしてみました。 NSURLSessionConfigurationの設定が反映されなくて困っている人や、iOS 9とそれ未満で挙動が違う問題にハマっている人の助けになれば幸いです。

参考