サーバからのResponseを意識して対応 〜 swift

#はじめに
NSURLConnectionを使ったコーディングの演習に、googleのURLを叩いたら必ず落ちる、というところから対応方法を検討しました。



#確認
文字コードのエンコーディング部分で落ちていることが分かり

```swift
let responseStr:NSString = NSString(data:resultResponseData, encoding:NSUTF8StringEncoding)!
```

SafariでのWebインスペクタを見てみました。

![スクリーンショット 2014-11-18 8.23.09.png](https://qiita-image-store.s3.amazonaws.com/0/12362/92293c61-9295-9a4c-d672-6116ba239899.png)

リソースで確認してみたところ

 * charsetはUTF-8
 * Content-Encodingはgzip

![スクリーンショット 2014-11-18 8.25.54.png](https://qiita-image-store.s3.amazonaws.com/0/12362/d351e9fd-9807-1892-1ba7-a7aedcf8e07e.png)


##HeaderFieldを追加

```swift
        // Do any additional setup after loading the view.
        let url:NSURL = NSURL(string:"http://google.co.jp")!
        var request:NSMutableURLRequest = NSMutableURLRequest(URL:url)
        request.addValue("utf-8", forHTTPHeaderField: "Accept-Charset")
        request.addValue("gzip", forHTTPHeaderField: "Accept-Encoding")
```
>Accepts-Encoding → Accept-Encodingに書き換えました

結果は変らずで落ちました。
>返ってくるresponseのヘッダは確認できます。結果に変化がなかったので、上記対応で良いのかは疑問が残ります

##response headerを見る

```swift
NSURLConnection.sendAsynchronousRequest(
            request,
            queue: NSOperationQueue.mainQueue(),
            completionHandler: {
            (response:NSURLResponse?, responseData:NSData?, error: NSError?) -> Void in
                if let resultError = error
                {
                    println(resultError.description)
                }

                if let httpResponse = response as? NSHTTPURLResponse {
                    let headers:NSDictionary = httpResponse.allHeaderFields

                    println("textEncodingName : \(httpResponse.textEncodingName) ");

                    println("----------------------------------------");
                    for obj in headers{
                        println("key : \(obj.key) value : \(obj.value)");
                    }
                    println("----------------------------------------");
                    let statusCode = httpResponse.statusCode
                    println("MIMEType : \(httpResponse.MIMEType)")
                    if statusCode != 200 {
                        println("sendAsynchronousRequest status code = \(statusCode); response = \(response)")
                    }
                }
        })

```

###結果
![スクリーンショット 2014-11-18 8.46.11.png](https://qiita-image-store.s3.amazonaws.com/0/12362/76bebcd1-3c4b-dc11-e251-d251ff690ac3.png)

Shift_JISが返ってきていました。gzipもされていない。

###サーバ側で受け取ったリクエスト
自前サーバのphpinfo()で取得した内容から
"Accept-Encoding"と"Accepts-Encoding"があるのは送信時に間違ったからですが、特に影響はないようでした。

```html
<tr><td class="e">_ENV["HTTP_HOST"]</td><td class="v">f-y.sakura.ne.jp</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPTS_ENCODING"]</td><td class="v">gzip</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT_CHARSET"]</td><td class="v">utf-8</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT"]</td><td class="v">*/*</td></tr>
<tr><td class="e">_ENV["HTTP_USER_AGENT"]</td><td class="v">[プロジェクト名]/1 CFNetwork/711.1.12 Darwin/13.4.0</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT_LANGUAGE"]</td><td class="v">en-us</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT_ENCODING"]</td><td class="v">gzip, deflate</td></tr>
<tr><td class="e">_ENV["SERVER_PROTOCOL"]</td><td class="v">HTTP/1.1</td></tr>
```

#対応
headerのtextEncodingNameに自動で対応させるように変更。

#実装例
```swift
        // Do any additional setup after loading the view.
        let url:NSURL = NSURL(string:"http://yahoo.co.jp")!
        var request:NSMutableURLRequest = NSMutableURLRequest(URL:url)
 
        NSURLConnection.sendAsynchronousRequest(
            request,
            queue: NSOperationQueue.mainQueue(),
            completionHandler: {
            (response:NSURLResponse?, responseData:NSData?, error: NSError?) -> Void in
                if let resultError = error
                {
                    println(resultError.description)
                }
            
                //Converting data to String
                
                var encoding: UInt = NSUTF8StringEncoding
                
                if let httpResponse = response as? NSHTTPURLResponse {
                    let headers:NSDictionary = httpResponse.allHeaderFields

                    println("textEncodingName : \(httpResponse.textEncodingName) ");
                    
                    if let textEncName = httpResponse.textEncodingName {
                        encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(textEncName))
                        
                        if encoding == UInt(kCFStringEncodingInvalidId) {
                            encoding = NSUTF8StringEncoding; // by default
                        }
                    }
                    
                    
                    println("----------------------------------------");
                    for obj in headers{
                        println("key : \(obj.key) value : \(obj.value)");
                    }
                    println("----------------------------------------");
                    let statusCode = httpResponse.statusCode
                    println("MIMEType : \(httpResponse.MIMEType)")
                    if statusCode != 200 {
                        println("sendAsynchronousRequest status code = \(statusCode); response = \(response)")
                    }
                }
                
                if let resultResponseData = responseData {
                    var stringEncoding : NSStringEncoding = NSUTF8StringEncoding
                    
                    if encoding != UInt(kCFStringEncodingInvalidId){
                        let responseStr:NSString = NSString(data:resultResponseData, encoding:encoding)!
                        //                    let responseStr:NSString = NSString(data:resultResponseData, encoding:NSUTF8StringEncoding)!
                        println(responseStr)
                    } else {
                        println("Content-Typeのcharsetを確認")
                    }
                }
```

#Content Negotiationについて

#参考URL

[Why] NSString の initWithData:encoding: で NSShiftJISStringEncoding を設定して nil となる．
https://hirooka.pro/?p=6205

https://github.com/mattdonnelly/Swifter/blob/master/Swifter/SwifterHTTPRequest.swift

6.1 HTTPリクエストの基礎
https://github.com/mixi-inc/iOSTraining/wiki/6.1-HTTPリクエストの基礎

コンテントネゴシエーション
http://httpd.apache.org/docs/2.2/content-negotiation.html

#さいごに
request時のacceptヘッダ系で都合の良いものを返してくれそうな気がしていたのですが
方法が見つからず（気のせいな可能性もあり）アプリ側をサーバの言いなりに合わせた対応になったのが納得がいかない部分です。

コンテンツネゴシエーションの対応は普通なのかと思ったら、そうでもなく。
Webpで使っているのを見るくらいです。
サーバ側を自前に変えての実装も別途調整してみます。

サンプルを公開してあるサイトの文字列エンコーディングには`NSUTF8StringEncoding`とあることが多く、暗黙の概念化していました。
また`NSShiftJISStringEncoding `について調べると、CP932とかガラケー時代の懐かしの文字コードが見つかって現役なのに驚きました。
でも、googleがShiftJISで返している理由は、分からないまま・・・。