Swift3, APIKit, ObjectMapper, SnapKit を使って Web API にアクセスしてみた件

最近は,Swift3 で iOS アプリを開発しているのですが,改めて使用しているライブラリについて調べてみた.
仕事では日々実装しなくてはいけないテーマがあり,なかなか全体を俯瞰する機会がないので,GW を期に確認してみたというところです.
ネット上で公開されているライブラリに関する記事をざっとチェックしましたが,公開から1年以上経っていることもあり,なかなかそのままのコードで動作させることが難しいようだったので,個人的なメモとして簡単なコードを書くことにしました.

では早速,サンプルで使用する Web API を選びます.

1. Web API を選ぶ

▼ お天気Webサービス仕様
http://weather.livedoor.com/weather_hacks/webservice
ライブドアで無料提供している Web API のようです.
以下のように「地域ID」を指定することで天気情報を取得できるようです.

(例)「福岡県・久留米の天気」を取得する場合は,以下のURLにアクセスしてJSONデータを取得できるとのこと.
http://weather.livedoor.com/forecast/webservice/json/v1?city=400040

簡単そうなので,これにします.

2. APIKit でリクエスト

Xcode 8.3.2 で Swift3 を使用し,シングルページアプリを作成します.
CocoaPods で,APIKit / ObjectMapper / SnapKit を導入します.

APIKit
https://github.com/ishkawa/APIKit
- iOS 8.0 以降に対応.

ObjectMapper
https://github.com/Hearst-DD/ObjectMapper

SnapKit
http://cocoadocs.org/docsets/SnapKit/0.10.0/index.html
https://github.com/SnapKit/SnapKit

Xcode で作成された ViewController.swift に Web API リクエストのコードを記述します.

1
2
3
4
5
6
7
8
9
10
11
12
private func sendWeatherAPI() {
    let request = WeatherRequest(city: 130010)
    Session.send(request) { [weak self] result in
        switch result {
        case .success(let response):
            self?.dispWetherResponse(dto: response)	// ビューに取得データを表示する
 
        case .failure(let error):
            print("error: \(error)")
        }
    }
}

API リクエストは WeatherRequest という名前にしました.
http://weather.livedoor.com/forecast/webservice/json/v1?city=400040 にアクセするするのでこんな感じでしょう.

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
import Foundation
import APIKit
import ObjectMapper
 
struct WeatherRequest: Request {
    typealias Response = WeatherResponse
    let city: Int
 
    init(city: Int) {
        self.city = city
    }
    var baseURL: URL {
        return URL(string: "http://weather.livedoor.com/forecast/webservice/json")!
    }
    var method: HTTPMethod {
        return .get
    }
    var path: String {
        return "v1"
    }
    var parameters: Any? {
        return nil
    }
    var queryParameters: [String: Any]? {
        return ["city": city.description]
    }
    func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response {
        guard let object = Mapper<Response>().map(JSONObject: object) else {
            return Response()
        }
        return object
    }
}

3. 取得したデータをマッピング

WebAPI で取得したデータが
func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response {
に渡されますので,struct にマッピングします.
また,ライブラリのバージョンがあがり,返却するデータの方がオプショナルでなくなっているようです.

とりあえず,3つのデータだけ格納することにします.
階層が一つ下の項目にも map[“description.text”] のようにで参照できるのが便利ですね.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Foundation
import ObjectMapper
 
struct WeatherResponse: Mappable {
    var publicTime = ""
    var title = ""
    var description = ""
 
    init() {
    }
    init?(map: Map) {
    }
 
    mutating func mapping(map: Map) {
        self.publicTime <- map["publicTime"]
        self.title <- map["title"]
        self.description <- map["description.text"]
    }
}

WeatherResponse が,ViewController.swift の Web API リクエスト部分のクロージャー記述に返されるので,画面に表示しておしまいです.

4. SnapKit で制約を指定する

あと,SnapKit で表示用ラベル位置を調整してみます.
SnapKit で制約を指定するわけですが,指定前にビューに対してラベルを addSubview() しておかなくてはなりません.

制約の指定は以下のようにしました.
一度,Autolayout の制約条件を Xcode から行いましたが,大変だった記憶があり,以下のコードだけで制約を指定できるのは大変便利かと思います.

1
2
3
4
5
6
7
8
9
10
titleLabel.snp.makeConstraints { make in
    make.top.equalTo(20)
    make.left.right.equalTo(view).inset(10)
    make.height.equalTo(30)
}
publicTimeLabel.snp.makeConstraints { make in
    make.top.equalTo(titleLabel.snp.bottom)
    make.left.right.equalTo(view).inset(10)
    make.height.equalTo(30)
}

titleLabel の下に publicTimeLabel が配置されるように制約を指定しています.
また,それぞれの高さは 30 です.

簡単にライブラリを使った検証でした.

▼ 追記

Himotoki っていうライブラリも結構紹介されているなぁーー
https://github.com/ikesyo/Himotoki