だいきちメモリアル

ブログです

Swiftで利用できるLooseCodableというライブラリを作った

LooseCodableというライブラリを作った。
CarthageとSwiftPackageManagerからインストールして利用することができる。

github.com

このライブラリを作ろうと思った動機だが、サーバーサイドのAPIレスポンスが安定していないというのが理由だ。

主にIntegerとString、IntegerとBooleanの型が様々な形式で返ってくるのだが、SwiftのCodable*1はこれらの型を曖昧に変換することを許さず例外を投げてくる。

例えば、Booleanで扱いたいケースで01で返ってくるケースとtruefalseで返ってくるケースが混在しているだとか、10で返ってくるか"10"で返ってくるかが混在しているかなど…

Codable登場以前は自前で書いたマッパーがこのあたりの差異をいい感じに吸収してくれていたのだが、Codableへ移行したと同時にこのような問題が顕在化してしまった。

Codableを利用すること自体は間違いのない選択だと考えているが、この気まぐれなAPIとは相性が悪い。
そこで、この気まぐれAPIを快く受け入れるべくLooseCodableを作ったというわけだ。


さて、このライブラリだが期待する型をLooseCodableという型で包んであげることでいい感じに差異を吸収してくれるものだ。

struct Hito: Codable {
    // このように定義しておくと
    let age: LooseCodable<Int>
}

// こんなデータが
let json = """
{
    "age": "10"
}
"""

// デコードできちゃいます!
let hito = try! JSONDecoder().decode(Hito.self, from: json.data(using: .utf8)!)
print(hito.age.value) // 10

上記のコードだとLooseCodableという型が表面に出てしまい、結果の値を得るためにvalueというプロパティを呼ぶのは、呼び出し側に多くの変更の負担が寄る可能性がありいただけない。
そもそもLooseCodableはCodableオブジェクトの外に見えている必要は無い。

このため定義するとき、LooseCodableを指定する型はプライベートにしておき、公開するプロパティをComputedにしたほうが良いだろう。

struct Hito {
    // LooseCodableなプロパティは非公開にし、
    private let _age: LooseCodable<Int>

    // 利用される値をComputedな公開プロパティにする
    var age: Int {
        return self._age.value
    }

    // JSONとプロパティのキーマッピングを忘れずに
    enum CodingKeys: String, CodingKey {
        case _age = "age"
    }
}

今回、初めてSwiftPackageManagerを利用してライブラリを公開してみたが、何もしなくてもCarthageでのインストールにも対応できていて中々に便利な代物だなと思えた。

ファイル構造を強制するなどとっつき憎い点もあったが、今後のSwiftにおけるライブラリ開発はSwiftPackageManagerで作成することを念頭に置いておきたいところだ。

*1:JSONデータをオブジェクトにマッピング等をしてくれる標準ライブラリ