はじめに
こんにちは、nukkyです。
私はSwiftでJSONを取り扱う際に、ObjectMapperを使用していたのですが、Swift4から追加されたCodableが便利そうなのでこちらを使用したいと思い備忘録がてら書いていこうと思います。
Codableとは
CodableはSwiftが提供している構造体やクラスなどをJSONに書き出す、読み込むためのプロトコル。(厳密に言うと、JSONだけではなく、他のフォーマットにも使えます) CodableはEncodable(書き出し用)とDecodable(読み込み用)の二つのプロトコルで構成されています。
Codable – Swift Standard Library | Apple Developer Documentation
Codable使いたい理由
いままで、JSONを取り扱おうと思ったらJSONをパースして、Dictionaryのキーを指定してデータクラスに代入(マッピング)するような流れを実装していたと思うのですが、これだとメンテナンス性も悪いうえコード量もそこそこになります、ライブラリを使用していても結局パース後のデータ構造への代入処理を記載したりマッピングは必須だったと思います。
ですがCodableであれば基本的にマッピングは不要になりコードもすっきりかけます!
実装
とりあえずJSONを読み込む
まずCodableで単純なJSONを読み込んで見たいと思います。
読み込むJSONは以下を用意します。
1 2 3 4 5 | { "id": "1", "name": "山田太郎", "age": 20 } |
データを入れる構造体を作成します。
1 2 3 4 5 | struct User: Codable { let id: String let name: String let age: Int } |
JSONデータをデコードして構造体に変換します。
1 2 3 4 5 6 7 | let decoder: JSONDecoder = JSONDecoder() do { let user: User = try decoder.decode(User.self, from: jsonData) print(user)// User(id: "1", name: "山田太郎", age: 20) } catch { print("error:", error.localizedDescription) } |
これだけです。printのログを見てもらえばわかりますがマッピング処理を一切書かずに済んでいます。
ObjectMapperの場合
比較用にObjectMapperの場合、同じJSONで処理内容は以下になります。
JSONは同じものを使用するとして構造体を作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | struct User: Mappable { var id: String! var name: String! var age: Int! init?(map: Map) { mapping(map: map) } mutating func mapping(map: Map) { id <- map["id"] name <- map["name"] age <- map["age"] } } |
JSONデータをデコードして構造体に変換します。
1 | let user: User? = Mapper<User>().mapArray(jsonData) |
マッピングをしなくてはならないため構造体の処理に明確な差が出てしまいますね。
GitHub – Hearst-DD/ObjectMapper
ネスト配列に対応してみよう
JSONデータがネストしている場合でも大丈夫です、Codableならネストも簡単です。
まずはJSONを用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { "id": "1", "name": "山田太郎", "age": 20, "photos": [ { "id": "1", "name": "photo1" }, { "id": "2", "name": "photo2" } ] } |
そうしたら「photos」が配列になっているので以下のようなデータ構造体を用意します。
1 2 3 4 5 6 7 8 9 10 11 | struct User: Codable { let id: String let name: String let age: Int let photos: [Photo] struct Photo: Codable { let id: String let name: String } } |
JSONデータをデコードして構造体に変換します。
1 2 3 4 5 6 7 | let decoder: JSONDecoder = JSONDecoder() do { let user: User = try decoder.decode(User.self, from: jsonData) print(user)// User(id: "1", name: "山田太郎", age: 20, photos: [ExampleProject.User.Photo(id: "1", name: "photo1"),ExampleProject.User.Photo(id: "2", name: "photo2")]) } catch { print("error:", error.localizedDescription) } |
JSONのキーがスネークケースの場合(Swift4.0)
Swiftの変数はキャメルケースですが、JSONのキーはスネークケースという場合も結構あります、その際はCodingKeysという機能で対応することが可能です。
JSONのキーはスネークケースのJSONを用意します。
1 2 3 4 | { "user_id": "1", "user_name": "山田太郎", } |
データを入れる構造体にCodingKeysを追加し作成します。
1 2 3 4 5 6 7 8 9 | struct User: Codable { let userId: String let userName: String private enum CodingKeys: String, CodingKey { case userId = "user_id" case userName = "user_name" } } |
JSONのキーがスネークケースの場合(Swift4.1)
結局マッピングみたいなことが必要なのかと思っていましたがSwift4.1からEncoderの「keyEncodingStrategy」やDecoderの「keyDecodingStrategy」を設定することで、キャメルケースのプロパティ名からスネークケースのキーが自動作成できるようになりました。
1 2 3 4 5 | let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase let encoder = JSONEncoder() encoder.keyEncodingStrategy = .convertToSnakeCase |
さいごに
Codableオススメですね。個人的には今までライブラリで行なっていたJSONの取り扱いは網羅できて、かつコード量も減るといいことづくめだと思います。Swift4以降で追加されれた機能など深掘りしていなかったのであらためていろいろチェックしていきます。これからiOS12/Xcode10も出ますしどんどん新機能を試してブログにしていきますね。
RE:ENGINESブログ「iOS記事」まとめページはこちらから