CocoaアプリでCookieを独自に管理する

CocoaのURL Loading Systemは通常、HTTP Cookieを自動的に管理してくれる。CookieはNSHTTPCookieというクラスにラップされ、NSHTTPCookieStorageというクラスがそれを溜め込んで自動的にどうこうしてくれる。しかし困るのは、CocoaCookieはNSHTTPCookieStorageを使う限り全アプリで共通だということだ。URL Loading Systemを直接叩くアプリでなくても、ちょっとWebKitを使ってWebページを表示するだけのアプリでも同様である。Safariで何かのログイン状態をCookieが保持していたら、Thousandでもそのままログインしていたりする、つまりはそういうことで困るのだ*1

では独自にCookieを管理したいときはどうすればいいのか。NSURLRequest に次のようなメソッドがある。

-(BOOL)HTTPShouldHandleCookies ;

デフォルトはYESで、その場合自動的にNSHTTPCookieStorageによるCookieの管理が行われる*2 *3。ので、これをNOにした上で自前でCookieの管理をすればいいはずである。NOにするにはNSMutableURLRequestを使う。

- (void)setHTTPShouldHandleCookies:(BOOL)handleCookies ;

次は自前でCookieの管理ってどうすればいいんだろうということになるが、NSHTTPCookieにあるメソッドである程度道筋がつく。

+ (NSArray *)cookiesWithResponseHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)theURL ;
+ (NSDictionary *)requestHeaderFieldsWithCookies:(NSArray *)cookies ;

NSHTTPURLResponseのヘッダーフィールドからCookieを生成したり、CookieからNSURLRequestのヘッダーフィールドへ値を設定したりはこれで出来る。そこでNSHTTPCookieを溜め込んだ上で、これらのメソッドを使っていろいろしてくれる、要は自前のNSHTTPCookieStorageっぽいものを作ってやることになる。

自前のNSHTTPCookieStorageは何をしなければならないか

ヘッダーフィールドとCookieのやりとりはこれで出来るとして、一番問題になりそうなのはURLに応じて適切なCookieを取り出してくることである。
ここを参考にして勉強してみた。

Studying HTTP: HTTP Cookies
http://www.studyinghttp.net/cookies

Cookieはえーと、まずドメインごとに持つことができる。

hoge.www.apple.com
www.apple.com
apple.com

このとき、URLのドメインの後方に一致したCookieを送ることになる。この場合だったら、hoge.www.apple.comを含むURLには、3つ全てのCookieを送ることになるのかな。

次に、ドメインの中でさらにパスごとにCookieを持つことになる。

/jp/macbook/hoge
/jp/macbook
/jp

このとき、パスの前方に一致してより詳細なCookieが、ヘッダーフィールドを上書きする。…上書き?クッキーの持つキーバリューのペアのうち重複するものが上書きされるということかな。

よってドメインとパス、2段階でCookieを検索出来る仕組みが必要である。私はNSMutableDictionaryの入れ子でやってみた。

追記

間違えた。NSHTTPCookieはキーバリューにつき一つだからえーと、ドメイン、パス、名前で3段階で検索するときもある。同じ名前でパスが違うNSHTTPCookieは、パスがより詳細に一致するほうが優先される、と。よし。

他にしなければいけないこと

  • 有効期限のチェック
  • セキュアかどうかのチェック
  • セッション限りだったら捨てる

まあここらへんは普通に出来そうだ。これでなんとか自前でCookieの管理が出来るのではないか?これから試してみる。

*1:しかもNSHTTPCookieStorageはCookieの受け入れポリシーまでも共通である…

*2:NSHTTPCookieStorageの共通インスタンスをアプリケーションごとに作れるようにしてくれれば、独自にしかも自動的にCookieの管理が出来るだろうに、何故だApple

*3:当然、手としてはNSHTTPCookieStorageのサブクラスを作った上で、カテゴリでsharedHTTPCookieStorageが独自のインスタンスを返すようにしてしまうというのが考えられる。けどNSHTTPCookieStorageの実装依存だと思う