LoginSignup
10
10

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-11-18

はじめに

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

確認

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

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

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

スクリーンショット 2014-11-18 8.23.09.png

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

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

スクリーンショット 2014-11-18 8.25.54.png

HeaderFieldを追加

        // 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を見る

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

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

サーバ側で受け取ったリクエスト

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

<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に自動で対応させるように変更。

実装例

        // 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

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で返している理由は、分からないまま・・・。

10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10