Galapagos Engineering Blog

株式会社ガラパゴス エンジニアチームによるブログです。

Swift 4でJSONの扱い

ガラパゴスのコードヒーヨアン(twitter: @luinily)です。

先月WWDCApple社のOS、SDKなどの新バージョンか公開されました。その中にSwift 4も発表されて、その中にJSONの扱いに関するツールが追加されたことがわかりました。

弊社では、サーバーとのやりとりを行うアプリの開発が珍しくありませんが、その際JSONを扱うことが多く、扱いやすくなると仕事がしやすくなります。 今回の記事ではSwift 4で導入される新しいJSON用のツールを紹介したいと思います。

JSONとは

JSONは軽量なデータ記述言語の1つです。JavaScriptから生まれ、サーバーとアプリの情報交換などによく使われるようになり、多くの言語で扱うツールが存在します。 下記のようなJSONの形式は人間に可読なテキストファイルです。

{
    "firstName": "オルシュファン",
    "lastName": "グレイストーン",
    "age": 28,
    "address": {
        "place": "ドラゴンヘッドt",
        "region": "クルザス中央地",
        "state": "イシュガルド",
    }
}

フォーマットの詳細は「JSON - Wikipedia」を参考してください。

今までの扱い

今までJSONを扱うときは下記のような扱いになっていた:

  1. JSONデータをData形式で取得します。
  2. そのデータを[String: Any]に変換します。
  3. ディクショナリーの各キーにあるデータを正しく変換してデータを取り出す。

難しいことはしていませんが、任意なキーがないときや、オブジェクトがあるときなど、いろんな場合に対しての処理を、変換処理に加えて対応する必要がありますので、実装が時間をかかってしまって、馬具しやすいコードになります。そのために現在いろんなライブラリーが存在していますが、SwiftやiOS SDKで正式なライブラリーがないため、プロジェクトによって違うライブラリーを使ったり、使用していたライブラリーの開発が止まったりますし、ライブラリーを使うことでうまく変換できないときデバッグが難しくなったりします。

Swift 4で追加されるCodable

Swift 4ではCodableというプロトコールとJSONDecoderというクラスが追加されます。それを使えば簡単いJSONの扱いができるようになります。 紹介するためにCodableとJSONDecoderを使う例を実装しようと思います。

JSON Feed

JSON Feedは5月に公開されたRSSのようなブログの更新情報を持つフォーマット。RSSXMLに比べて、JSONで定義されていて扱いやすい、データ量の軽いフォーマットだと思われます。 5月に発表されていて、すでに複数のブログまとめサービスなどで対応されています。実装は結構簡単なので今回の例にちょうどいいと思います。詳しいことは公式サイトで確認できます。

JSON FeedのJSONファイルの簡単な例を見ましょう

{
    "version": "https://jsonfeed.org/version/1",
    "title": "My Example Feed",
    "home_page_url": "https://example.org/",
    "feed_url": "https://example.org/feed.json",
    "items": [
        {
            "id": "2",
            "content_text": "This is a second item.",
            "url": "https://example.org/second-item"
        },
        {
            "id": "1",
            "content_html": "<p>Hello, world!</p>",
            "url": "https://example.org/initial-post"
        }
    ]
}

フェードの中にはversion、title、home_page_url、feed_urlのフィードの情報があります。 最後にitemsという配列の中には各記事の情報があります:id、url、テキストフォーマットでのコンテンツ、またはHTMLフォーマットでのコンテンツ このファイルを取得してブログのフィードの情報と、各記事の方法を取得できます。

アプリの中に、このJSONをどうすれば、Swiftの情報に変換できるか見て見ましょう。

データを入れるための構造体を作成します

まずはこのデータを入れるための構造体を書きます。

struct JsonFeed {
    var version: URL
    var title: String
    var homePage: URL?
    var feed: URL?
    var items: [JsonFeedItem]
}

version、homePage、feedはURLで定義します。JSON Feedの定義にはhomePageとfeedが任意だと書いてありますので、入ってない場合に備えてオプショナルの「URL?」として定義します。titleはそのままStringとして定義して、itemsは記事の情報の配列なので、配列として定義します。 記事の情報を持つために同じく構造体を作成します。

struct JsonFeedItem {
    var id: String
    var url: URL?
    var contentHTML: String?
    var contentText: String?
}

ここはフィードの方と同じく、任意の項目をオプショナルとして定義します。

JSONのデータを構造体に入れる

ここが関心のところです、CodableとJSONDecoderを使用し、作った構造体にJSONが持っているデータを入れます。 手順は簡単です。 まずは構造体にCodableを追加します。

struct JsonFeed: Codable {
    var version: URL
    var title: String
    var homePage: URL?
    var feed: URL?
    var items: [JsonFeedItem]
}

struct JsonFeedItem: Codable {
    var id: String
    var url: URL?
    var contentHTML: String?
    var contentText: String?
}

そうしたら、JSONDecoderを使用し、変換をします。

let decoder = JSONDecoder()
let feed = try? decoder.decode(JsonFeed.self, from: data)

変換が成功した場合、JsonFeed型のfeedにデータが入っています。それだけです!

キーのマッチング

ただし、このまま変換すると変換が失敗してしまいます。 理由は構造体のメンバーにJSONの定義と違う名称をつけたからです。 JSONで「home_page_url」となっているところを「homePage」とSwiftっぽい定義しました。JSONDecoderはメンバーとJSONのキーの名称が同じだと自動的に入れてくれますが、名称が違う場合、どのキーがどのメンバーになるのか教える必要があります。

実はCodableプロトコルを適用すると構造体の中に暗黙的にCodingKeysという列挙型が作成されます。この列挙型を上書きすることで、構造体のメンバーにどのキーを適用させるかのを定義できます。 JsonFeedとJsonFeedItemでCondingKeysを定義しましょう。

struct JsonFeed: Codable {
    var version: URL
    var title: String
    var homePage: URL?
    var feed: URL?
    var items: [JsonFeedItem]

    enum CodingKeys: String, CodingKey {
        case version
        case title
        case homePage = "home_page_url"
        case feed = "feed_url"
        case items
    }
}

struct JsonFeedItem: Codable {
    var id: String
    var url: URL?
    var contentHTML: String?
    var contentText: String?

    enum CodingKeys: String, CodingKey {
        case id
        case url
        case contentHTML = "content_html"
        case contentText = "content_text"
    }
}

列挙型は「enum CodingKeys: String, CodingKey」として定義をします。構造体の全てのメンバーのcaseを追加する必要があります。 メンバーとキーの名称が同じであれば、メンバーと同じ名称のcaseだけを定義します メンバーとキーの名称が異なる場合は、メンバーと同じ名称のcaseを定義して、そのcaseにキーの名称をStringとして定義します。 この修正を適用するとJSONデータの変換が成功します。

まとめ

CodableとJSONDecoderを使う場合、JSONの変換に必要な手順は

  1. JSONの情報を持つための構造体を作成する。
  2. その構造体にCodableプロトコルを定義する。
  3. 構造体のメンバーの名称がJSONのキーの名称と異なる場合。CodingKeysの列挙型を定義する
  4. JSONDecodeのインスターンスを作成する
  5. Dataとしてもつ JSONデータをJSONDecode.decodeで変換させる

構造体の定義は以前でも必要がありましたが、CodableとJSONDecoderのおかげで変換コードが2行だけになりました。自作のアプリにCodableを適用して見たら、80行ぐらいあった変換コードがたったの2行になりました。

Codable関連はWWDC「What’s new in Foundation」で発表されました。 9月からSwift 4がリリースされたら、業務で使うのが楽しみです。