Tech Blog

グローバルな家族アプリFammを運営するTimers inc (タイマーズ) の公式Tech Blogです。弊社のエンジニアリングを支える記事を随時公開。エンジニア絶賛採用中!→ https://timers-inc.com/engineering

Swift 4からFoundationに採用されるCodableプロトコルに感動した #wwdc2017

引き続きWWDC 2017に参加中のかっくん(@fromkk)です。
今朝聴講した What's New in Foundation というセッションで発表されていたSwift 4からFoundationに採用される Codable プロトコルに感動したので簡単に紹介したいと思います。

定義

プロトコルの定義はこんな感じです。

public protocol Encodable {
    public func encode(to encoder: Encoder) throws
}

public protocol Decodable {
    public init(from decoder: Decoder) throws
}

public typealias Codable = Decodable & Encodable

前提

この様なデータをダウンロードする事を想定しています。

{
    "hello": "world",
    "key": null,
    "int": 12345,
    "double": 9876.543,
    "url": "http://timers-inc.com/",
}

これまで

これまでAPIとの通信をしてモデルを作成する場合、下記の様な記述をする事が多かったのでは無いでしょうか?

struct OldJson {
    let hello: String
    let key: String?
    let int: Int
    let double: Double
    let url: URL
}

let data: Data = ...
    
do {
    guard let json: [AnyHashable: Any] = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable: Any] else {
        print("json convert failed")
        return
    }
    
    guard let hello: String = json["hello"] as? String,
        let int: Int = json["int"] as? Int,
        let double: Double = json["double"] as? Double,
        let urlString: String = json["url"] as? String,
        let url: URL = URL(string: urlString) else {
            print("get values failed")
            return
    }
    
    let oldJson = OldJson(hello: hello, key: json["key"] as? String, int: int, double: double, url: url)
    print(oldJson) //Success!!!!
} catch {
    print("json convert failed in JSONSerialization", error.localizedDescription)
}

とても長ったらしくて毎回書くのは大変ですよね。
なので APIKitHimotoki といったライブラリを利用して共通化する事が多かったかと思います。

Swift 4以降

Swift 4のFoundationから採用される Codable を利用すると次の様になります。

struct NewJson: Codable {
    let hello: String
    let key: String?
    let int: Int
    let double: Double
    let url: URL
}

let data: Data = ...

let decoder: JSONDecoder = JSONDecoder()
do {
    let newJson: NewJson = try decoder.decode(NewJson.self, from: data)
    print(newJson) //Success!!!
} catch {
    print("json convert failed in JSONDecoder", error.localizedDescription)
}

Codable が勝手にマッピングしてくれるのでとても短くなりました!
ここまで簡素化出来ると他にやるべき事に集中出来る様になりますね!

また、嬉しい機能としてネストも可能です。

struct Json: Codable {
    let hello: String
    let url: URL
    
    struct Nested: Codable {
        let hoge: String
        let fuga: String
    }
    let nested: Nested
}

let str: String = """
{
    "hello": "world",
    "url": "http://timers-inc.com/",
    "nested": {
        "hoge": "hogehoge",
        "fuga": "fugafuga"
    }
}
"""

JSONのキーとモデルのプロパティ名が異なる場合

サーバーサイドではキーをスネークケースで定義していて、クライアント側ではキャメルケースでプロパティ名を定義しているなんて事もありますよね。
そんな時は CodingKey を利用すれば解決出来ます。

JSONがこういう時、

{
    "hello": "world",
    "snake_case": "snake case string"
}

こうする事でSwiftサイドでは camelCase というプロパティに snake_case というキー名を適用させる事が出来ます。

struct Json: Codable {
    let hello: String
    let camelCase: String
    
    private enum CodingKeys: String, CodingKey {
        case hello
        case camelCase = "snake_case"
    }
}

まとめ

正式リリースはまだなので変更される可能性はありますが、現時点でもかなり便利な機能ですね。
個人的にはマッピングの処理はかなり面倒なのでここが自動化されるだけでかなりコーディングが捗るなと感じています。
正式リリースが楽しみですね!!


積極採用中!!

子育て家族アプリFammカップル専用アプリPairyを運営するTimers inc. では、現在エンジニアを積極採用中! 急成長中のサービスの技術の話を少しでも聞いてみたい方、スタートアップで働きたい方など、是非お気軽にご連絡ください! 採用HP : http://timers-inc.com/engineerings

Timersでは各職種を積極採用中!

急成長スタートアップで、最高のものづくりをしよう。

募集の詳細をみる