カテゴリー: iOS

[Swift4]ライブラリをやめてCodableでJSONを取り扱う

はじめに

こんにちは、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は以下を用意します。

{
    "id": "1",
    "name": "山田太郎",
    "age": 20
}

データを入れる構造体を作成します。

struct User: Codable {
    let id: String
    let name: String
    let age: Int
}

JSONデータをデコードして構造体に変換します。

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は同じものを使用するとして構造体を作成します。

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データをデコードして構造体に変換します。

let user: User? = Mapper<User>().mapArray(jsonData)

マッピングをしなくてはならないため構造体の処理に明確な差が出てしまいますね。
GitHub – Hearst-DD/ObjectMapper

ネスト配列に対応してみよう

JSONデータがネストしている場合でも大丈夫です、Codableならネストも簡単です。
まずはJSONを用意します。

{
    "id": "1",
    "name": "山田太郎",
    "age": 20,
    "photos": [
        {
            "id": "1",
            "name": "photo1"
        },
        {
            "id": "2",
            "name": "photo2"
        }
    ]
}

そうしたら「photos」が配列になっているので以下のようなデータ構造体を用意します。

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データをデコードして構造体に変換します。

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を用意します。

{
    "user_id": "1",
    "user_name": "山田太郎",
}

データを入れる構造体にCodingKeysを追加し作成します。

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」を設定することで、キャメルケースのプロパティ名からスネークケースのキーが自動作成できるようになりました。

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase

さいごに

Codableオススメですね。個人的には今までライブラリで行なっていたJSONの取り扱いは網羅できて、かつコード量も減るといいことづくめだと思います。Swift4以降で追加されれた機能など深掘りしていなかったのであらためていろいろチェックしていきます。これからiOS12/Xcode10も出ますしどんどん新機能を試してブログにしていきますね。

RE:ENGINESブログ「iOS記事」まとめページはこちらから

参考

nukky

シェア
執筆者:
nukky
タグ: Swift

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前