APIからデータを取得する時、JSONでデータを受け取ることがあるかと思います。JSONでデータを受け取った時、受け取り側でモデルに変換してデータを扱うことがあります。Swift 4から提供されているCodableを使うことでモデルへの変換が簡単にできます。
今回は、Codableについて紹介します。
Codableとは?
Swift 4から提供されているプロトコルの1つで、JSONからモデルへ変換するときのように異なるフォーマットへ変換するのをサポートするプロトコルです。API通信などで受け取ったJSONデータとモデルを相互変換するのに非常に役に立ちます。
検証環境
- Xcode 10
- QuickとNimbleを利用して検証
Codableを使ったモデルの実装
1. JSONをCodableを使ってモデルに変換する
今回は、Qiita APIのユーザーモデルを利用します。まずは、Requiredなプロパティをモデルに定義します。
import Foundation struct User: Codable { let id: String let permanentId: String let profileImageUrl: String let itemsCount: Int let followeesCount: Int let followersCount: Int }
Codableを使って変換するテストを書いて確認して見ます。
import Quick import Nimble class UserTest: QuickSpec { override func spec() { describe("Parsing json to User") { it("is success") { let data = """ { "id" : "yaotti", "permanentId": 1, "profileImageUrl": "https://goo.gl/kqfaHF", "itemsCount": 300, "followeesCount": 100, "followersCount": 200, } """.data(using: .utf8)! let user = try! JSONDecoder().decode(User.self, from: data) dump(user) expect(user.id).to(equal("yaotti")) expect(user.permanentId).to(equal(1)) expect(user.profileImageUrl).to(equal("https://goo.gl/kqfaHF")) expect(user.itemsCount).to(equal(300)) expect(user.followeesCount).to(equal(100)) expect(user.followersCount).to(equal(200)) } } } }
実行すると以下のように、JSONからユーザーモデルへ変換できています。
▿ sampleTests.User - id: "yaotti" - permanentId: 1 - profileImageUrl: "https://goo.gl/kqfaHF" - itemsCount: 300 - followeesCount: 100 - followersCount: 200 Result > Test Suite 'All tests' passed at 2018-10-13 18:21:49.367. > Executed 1 test, with 0 failures (0 unexpected) in 6.263 (6.269) seconds
2. JSONキーをスネークケースに対応する
サンプルで書いているJSONデータのキーはキャメルケースになっています。しかし、APIで取得するプロパティのキーはスネークケースなのでスネークケースに修正します。
スネークケースで利用する場合は、CodingKeysに対応するキーを設定します。対応づけが必要ない場合は、キーは省略できます。
struct User: Codable { /* 中略 */ enum CodingKeys: String, CodingKey { case id case permanentId = "permanent_id" case profileImageUrl = "profile_image_url" case itemsCount = "items_count" case followeesCount = "followees_count" case followersCount = "followers_count" } }
テストのJSONデータも以下のように修正します。
let data = """ { "id" : "yaotti", "permanent_id": 1, "profile_image_url": "https://goo.gl/kqfaHF", "items_count": 300, "followees_count": 100, "followers_count": 200, } """.data(using: .utf8)!
先ほどと同様にテストを実行すると、以下のような結果になり成功します。
▿ sampleTests.User - id: "yaotti" - permanentId: 1 - profileImageUrl: "https://goo.gl/kqfaHF" - itemsCount: 300 - followeesCount: 100 - followersCount: 200 Result > Test Suite 'All tests' passed at 2018-10-13 18:34:14.015. > Executed 1 test, with 0 failures (0 unexpected) in 5.543 (5.552) seconds
3. nullで渡ってくるケースにも対応する
APIを利用するとき、全ての値が必ず値を持っているとは限りません。その時は、Optionalを使って値をnilで保持できるようにしましょう。今回は、facebookIdを取得してみます。この値は、Facebookをリンクしていないと取得できません。
struct User: Codable { /* 中略 */ /* Option */ let facebookId: String? enum CodingKeys: String, CodingKey { /* 中略 */ case facebookId = "facebook_id" } }
データにfacebook_idを追加して、nullをセットするようにします。
let data = """ { ... "facebook_id": null } """.data(using: .utf8)! /* 中略 */ expect(user.facebookId).to(beNil())
先ほどと同様にテストを実行すると、以下のような結果になり成功します。facebookIdにnilがセットされていますね。
▿ sampleTests.User - id: "yaotti" - permanentId: 1 - profileImageUrl: "https://goo.gl/kqfaHF" - itemsCount: 300 - followeesCount: 100 - followersCount: 200 - facebookId: nil Result > Test Suite 'All tests' passed at 2018-10-13 19:34:24.939. > Executed 1 test, with 0 failures (0 unexpected) in 5.954 (5.959)
まとめ
Codableを使うことで、DictionaryやSwiftyJSONを使うといったことなくJSONデータを簡単に変換できるようになります。また、工夫をすれば特殊な変換するロジックを挟むこともできる様になります。Codableは非常に便利なのでおすすめです。