なんとかするから、なんとかなる

エンジニア関係のことを書きます

iOS Decodableの基本的な使い方

English version below

はじめに

今回はSwift4 で追加されたDecodableの使い方について紹介します。  

Decodableとは

API Requestなどで返ってくるJSONのData型を任意のクラスや構造体に変換する際に便利です。

これまではSwiftyJSONなどライブラリーを使ってJSONの構造体にアクセスすることが多かったと思います。

しかし、そのままSwiftyJSONを使う場合、

  • 連想配列Dictionary に対して文字列を渡すことでタイポの危険性を伴う
  • 意図しないJSONでもJSONであれば問題無い

など、もやもやが僕の中にはありました。

Decodableを使うことで決まったJSONを決まったクラス/構造体にしか変換しないので、とてもわかりやすくて好きです。

Decodableを使って見る

構造体に対してDecodableのProtocolを適用する。

 

struct SomeStruct: Decodable {
    var value: String
    var version: Int
    
    enum CodingKeys: String, CodingKey {
        case value
        case version = "app_version"
    }
}

 

試しにデコードして見る

 

let json: Data = """
{
    "value": "GoodString",
    "app_version": 5
}
""".data(using: .utf8)!
let someStruct = try? JSONDecoder().decode(SomeStruct.self, from: json)

 

詳細

構造体に対して、Decodableの適用を宣言。

CodingKeysという列挙体を宣言すると完了です。

CodingKeysはrawValueがStringの列挙体で、JSONの各Keyがどの変数に対応しているのかを宣言します。

CodingKeysのrawValueが変数名と等しい場合は、変数名をそのまま列挙体でも使用、異なる場合はrawValueに対してKey名を対応させます。

構造体の各変数はvarで宣言しないと、Decodeに失敗しnilが返ってきます。

hopita.hatenablog.com

JSONのKeyで存在の有無がOptionalの場合は構造体の変数宣言をOptionalにすればOKです。

struct SomeStruct: Decodable {
    var value: String
    var version: Int
    var exception: Int?
    
    enum CodingKeys: String, CodingKey {
        case value
        case version = "app_version"
    }
}

 

階層のあるJSONをデコードする

次に、階層構造のあるJSONについてです。 上記のSomeStruct の中にInnerStructが配列で存在すること場合、次のように宣言します。  

Decodableの宣言

 

struct SomeStruct: Decodable {
    var value: String
    var version: Int
    var inners: [InnerStruct]
    
    enum CodingKeys: String, CodingKey {
        case value
        case version = "app_version"
        case inners = "inner_structs"
    }
}
 
struct InnerStruct: Decodable {
    var index: Int
    var name: String
    
    enum CodingKeys: String, CodingKey {
        case index = "inner_index"
        case name = "inner_name"
    }
}

  今回はInnerStructは配列ですが、InnerStruct単体であれば[]を外せばOKです。  

デコードする

 

let json: Data = """
{
    "value": "GoodString",
    "app_version": 5,
    "inner_structs": [{
        "inner_index": 1,
        "inner_name": "myName"
    }]
}
""".data(using: .utf8)!
let someStruct = try? JSONDecoder().decode(SomeStruct.self, from: json)

 

おわりに

とても便利です。簡単です。使っていきましょう。