2015年3月3日火曜日

はじめてのWebAPI〜基礎とSwiftでの実装〜

APIとは、あるデータや機能を、他のプログラム(外部)から呼び出すためのインターフェースの仕様を示します。
そのうち、Web上にあるプログラムを用いるものがWeb APIです。

APIを用いることで、到底一人のプログラマには集められないようなデータを使えたり、機能を使えたりします。
APIとしては本当に色々あり、TwitterAPIやHOT PEPPER API、Google Maps API、Yahoo!のAPIなど、各社が様々なAPIを提供しています。
参考:ライブラリ・フレームワーク・APIの違い

APIを使う側についての記事です。

まず、身近な例として、普段見ているwebサイトについて。このブログの場合、
アドレス”http://ha1f-blog.blogspot.jp/”を入力すると、そのアドレスにあるサーバーに対し、HTTPリクエスト(ページの要求)が送信されます。それに対し、サーバーが応答し、HTTPレスポンスとして、ページの内容や画像を返します。
参考:IPアドレスとは
参考:Webサイト高速化に必要なHTTPの仕組みを理解する


そして、送られてくるデータは文字がずらずら並んだデータです。つまり、右クリックから「ページのソースを表示」をしたときに表示されるもの

こんな感じで見れます。(このブログでの例、HTML形式で記述されています)
これがこのブログの正体?です。このデータをブラウザが解析して、キレイに表示しています。

大体見れたかと思いますが、ネットからデータを取ってくる際に、データを取ってくるのが第一の課題、取ってきたデータをどう処理するか(パース)が第二の課題になります。


今回はRECRUIT Webサービスの一つ、HOT PEPPERのグルメサーチAPIを例に取ります。
ここから利用規約を読んで、登録すると、APIキーを発行することができます。

以降、keyは****************として表記しますが、自分で取得したキーを入れてください。

<第一の課題>
とりあえず
http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=****************&format=json&lat=35.6800079345703&lng=139.768936157227
をブラウザに入力してみると、

こんな感じのデータが見れるかと思います。(今回はJSON形式のデータです)
ブラウザを使うと、データを取ってくるのは簡単ですね。

これをiPhone(Swift)で実現するには

// use NSURLSession
        var url: NSURL = NSURL(string:"http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=****************&format=json&lat=35.6800079345703&lng=139.768936157227")!
        var myRequest = NSMutableURLRequest(URL:  url)
        myRequest.HTTPMethod = "GET" 
        var task = NSURLSession.sharedSession().dataTaskWithRequest(myRequest, completionHandler: { data, response, error in
            if (error == nil) {
                //正常終了、レスポンスはdataに
                println(NSString(data:data, encoding:NSUTF8StringEncoding))
                }
            } else {
                println(error)
            }
        })
        task.resume()

このように記述します。

NSURLSessionを用いています。

NSURLConnectionは色々な通信を行うことができますが、http通信であれば、簡略化したクラスNSURLSessionを用いたほうが、楽に非同期通信を実現できます。
※非同期通信→並列処理。データを取ってくるのに時間がかかることが多いため、その分プログラムの動作を停止するのはもったいない。よって、バックグラウンドでも実行できるようにこの方式を使うことが多い。
参考:非同期通信

NSURL()によって文字で書かれたurlをurlの形式にエンコード

HTTPリクエストの形式は"GET"(受信)と"POST"(送信)の2つがありますが、今回はデータをPOSTする必要はないので、"GET"にします。これをNSMutableURLRequestの.HTTPMethodメソッドによって、行います。

そして、NSURLSession.sharedSession().dataTaskWithRequestによってタスクを生成し、task.resume()でそのタスクを開始しています。
つまり、生成されたtaskの中の命令が、task.resume以降にバックグラウンドで実行され始めます。

これで第一の課題終了です!と行きたいところですが、肝心のURL部分について説明していません。笑
URLはAPIリファレンスを見ながら考えます。

今回使ったURLは、
http://webservice.recruit.co.jp/hotpepper/gourmet/v1/
?
key=****************
&
format=json
&
lat=35.6800079345703
&
lng=139.768936157227
と分解することができます。
これで、JSON形式で緯度経度が(35.6800079345703, 139.768936157227)周辺の店データを取得できます。

一つ目はアドレスで、?以降は引数のようなものになります。(引数名)=(内容)として、&でつないでいきます。どんな引数があるかは、リファレンスに示されています。

まずリクルートのAPIを使う場合は、key=****************が必須です。
formatは、標準はxmlとなっていますが、Swiftの場合はJSONのほうが扱いやすいので、JSONにしています。
lat=?とlng=?で緯度経度を指定しています。
他にも色々試してみましょう!

日本語を入れたい場合は、
        let query: String = textinput.text
        let encodedQuery: String = query.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
        var urlstring = "http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=****************&format=json&name=\(encodedQuery)"
こんな感じで、文字をエンコードしてから入れる必要があります。アドレスバーでよく見る%xx%xx%xxみたいな形式に変換されます。

これで本当に第一の課題完了です!


<第二の課題>
上のプログラムで、「//正常終了、レスポンスはdataに」の部分に、正常にデータを取得できた時の命令を足していきましょう。

まず取得したデータをそのまま見ると、(ブラウザの場合の画像参照)まあわけわからんぐらい長いデータが続いています。これをSwiftで解析するときには、SwiftyJSONが便利です。
SwiftyJSONはGitHubに公開されているので、こちらから利用させて頂きます。ここから、Source/SwiftyJSON.swiftをダウンロードして、プロジェクトフォルダ内にいれ、Drag&Dropでプロジェクト内に入れておきます。
これだけで使用準備は完了です。

あとは正常終了時のコードとして、
                let json = JSON(data: data)
                for i in 0...9{
                    if let stringdata = json["results"]["shop"][i]["name"].string{
                        NSLog(stringdata)
                    }
                }
を追加します。これでshopの名前が10個、出力されます。

一番上の行だけでJSONを解析してくれるので、あとはリファレンスを参照しながら、取り出したいデータを見ていきます。基本は[]の内部の値(辞書のキー)を変えるだけでOKです。(ブラウザで取得されたデータを見てもわかりやすいかもしれません)

標準では10件の店データが取得されるので、i=0〜9のデータを見ています。

web上の画像からUIImageを生成したい場合は、
                        let imgurl = NSURL(string: json["results"]["shop"][i]["logo_image"].string!)
                        var err: NSError?
                        var imageData :NSData = NSData(contentsOfURL: imgurl!,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)!;
                        var img = UIImage(data:imageData)!
このようにします。

第二の課題は、SwiftyJSONのお陰で、とても簡単に解決出来ましたね!

本記事は以上です!

0 件のコメント:

コメントを投稿