Takahiro Octopress Blog

-1から始める情弱プログラミング

Swift Alamofireライブラリを使ってみよう!

通信系ライブラリAlamofireの使い方を覚えよう!

さて、本日は Alamofire ライブラリの使い方を勉強します。
AlamofireはObjective-Cの超有名通信系ライブラリであるAFNetworkingのSwift版と思えばよいでしょう。

導入の仕方

まずは導入の仕方から見て行きましょう。
CocoaPodsを使います。
Podfileを作成し、下記の内容を記載しましょう。

1
2
3
4
5
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'Alamofire', '~> 1.2'

これでpod installすればプロジェクトにAlamofireが追加されるはずです!
後は利用したいswiftファイルでimport Alamofireしましょう。
SwiftライブラリをCocoaPodsでインストールすると対象iOSが8.0以上である必要が出てくるので、そこだけ注意しましょう。

GETリクエスト(パターン1)

続いて、GETリクエストのメソッドを書いてみます。
以前のAFNetworking2.xでWeb-APIを叩いてみた!でも利用した OpenWeatherMap API を例として使います。

http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jpでリクエストして、
下記のデータを返してもらうことを想定しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
  base:  "stations",
  clouds: {
      all: 32
  },
  cod: 200,
  coord: {
      lat: 35.69,
      lon: 139.65
  },
  dt: 1432394992,
  id: 1850147,
  main: {
      grnd_level: 1019.97,
      humidity: 88,
      pressure: 1019.97,
      sea_level: 1024.92,
      temp: 290.99,
      temp_max: 290.99,
      temp_min: 290.99
  },
  name: Tokyo,
  sys: {
      country: "JP",
      message: 0.0386,
      sunrise: 1432323041,
      sunset: 1432374314
  },
  weather: [
      {
          description: "scattered clouds",
          icon: "03n",
          id: 802,
          main: "Clouds"
      }
  ],
  wind: {
      deg: 198.001,
      speed: 5.11
  }
}

では、GETリクエストを投げてみましょう。
返却されたデータの取得方法についても記載します。
幾つか書き方があるのですが、まずは1つ目の書き方です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// GETリクエスト
Alamofire.request(.GET, "http://api.openweathermap.org/data/2.5/weather", parameters:["q":"Tokyo,jp"]).response { (request, response, data, error) in
  
  if let data = data as? NSData {
      // AnyObject型をNSData型に変換できた場合に通過
      // NSData型をJSONの形に変換して、AnyObject?型の変数に格納
      var obj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil)

      if let weatherArray = obj["weather"] as? [AnyObject] {
          // objからweatherフィールドを取得。その際、Array型に変換できた場合に通過
          if let weather = weatherArray[0] as? [String: AnyObject] {
              // NSArray型のweatherArrayから要素0番目を取得。その際、Dictionary型に変換できた場合に通過
              if let description = weather["description"] as? String {
                  // NSDictionary型のweatherからdescriptionフィールドを取得。その際、String型に変換できた場合に通過
                  println(description)
              }

              // NSDictionary型のweatherからdescriptionを指定して取得
              var description2:String? = weather["description"] as? String
              println(description2)
          }
      }
  }
}

注意したいのは、responseで返却されるdataNSData型 ではなく、 AnyObject?型 だということです。
そのため、直接NSJSONSerialization.JSONObjectWithDataの1つ目の引数にdataを指定するとNSData型ではないことを示唆するエラーが出ます。
なので、 NSData型 に変換する必要があります。

そして、NSData型のままでは各要素を取り出せないので、 (JSONの形をした)AnyObject?型 に変換させましょう。
(Dictionary型に変換しても良いのですが、既にJSONの形に変換されているので十分でしょう。)
後は各フィールドの型に従って変換して取り出していくイメージで書きましょう。

覚えておきたいのは、

1
2
3
if let 定数名 = 要素 as? 定数の型 {
  // 指定した定数の型に変換できた場合に、この中を通過する(変換できなかった場合はnilを入れようとするので、この中を通過しない)
}

という変換の書き方です。
Swiftの場合、JSONの取り扱いで思った以上にコードが長くなり、ネスト化してしまいます。
それを防ぐため&JavaScriptのような書き方を意識したライブラリがたくさん出ています。
近いうち使ってみようと思います。

また、変換時に[AnyObject][String: AnyObject]と書いているのは、
前者がArray型に変換していて、後者はDictionary型に変換しています。
Swiftではどんな要素を含むArray型なのかを示し、どんなkeyでどんなvalueなのかをDictionary型として明示的に示す必要があります。

もし、Objective-CのNSArray型NSDictionary型に変換する場合は中身の形式を考えずにasの後ろに続けて書けばOKです。

GETリクエスト(パターン2)

とは言え、少しネストが深いので、別のresoponse形式のパターンで試してみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GETリクエスト
Alamofire.request(.GET, "http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp").responseJSON { (_, _, JSON, _) in
  if let weatherArray = JSON!["weather"] as? [AnyObject] {
      // JSONからweatherフィールドを取得。その際、Array型に変換できた場合に通過
      if let weather = weatherArray[0] as? [String: AnyObject] {
          // NSArray型のweatherArrayから要素0番目を取得。その際、Dictionary型に変換できた場合に通過
          if let description = weather["description"] as? String {
              // NSDictionary型のweatherからdescriptionフィールドを取得。その際、String型に変換できた場合に通過
              println(description)
          }

          // NSDictionary型のweatherからdescriptionを指定して取得
          var description2:String? = weather["description"] as? String
          println(description2)
      }
  }
}

これは、レスポンスメソッドにresponseJSONを指定することで、
1つ目に上げたAnyObject?型 ⇒ NSData型への変換とNSData型 ⇒ JSONの形をしたAnyObject?型への変換は不要となります。
既にresponseJSONの中でやってくれています。
少しネストが浅くなりましたね!

GETリクエスト(パターン3)

まだ、ネストが深いって?
よし!思い切ってネストをなくしましょう!

1
2
3
4
5
6
7
8
9
// GETリクエスト
Alamofire.request(.GET, "http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp").response { (request, response, data, error) in
  // AnyObject?型をNSData型に変換。さらにJSONの形をしたNSDictionary型に変換。
  let json = NSJSONSerialization.JSONObjectWithData(data as! NSData, options: NSJSONReadingOptions.MutableContainers, error: nil) as! [String: AnyObject]
  let weatherArray = json["weather"] as! [AnyObject]
  let weather = weatherArray[0] as! [String: AnyObject]
  let description = weather["description"] as! String
  println(description)
}

う〜ん。ネストは確かになくなったのですが、Unwrapの手法を Implicitly Unwrapped Optional にしたため、nilが発生した段階でエラーが発生して落ちます…。
値がない場合には空オブジェクトを返すAPIである場合には問題ないと思います。

だんだんとSwiftの基礎含めて理解が進んできた気がします。
と言ったところで本日はここまで。

Comments