Swift Decodable(Codable) 　色んなJSONデータパターンのまとめ  【備忘録】

自分がAPIから取得したJSONのレスポンスパラメータで、色々な構造パターンがあったので備忘録も兼ねてまとめてみたいと思います。

### そのままプロパティに適用できるパターン

一番手間がない形式でシンプルにデコード用クラスを定義できる

```Swift
var data = """
{
  "id": 1,
  "name": "Taro",
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name: String
}

let user: User = try JSONDecoder().decode(User.self, from: data)
print(user)
```

### キーとプロパティが一致しないパターン

Swiftの変数名の命名規則はローワーキャメルケースなので、CodingKeyを使って揃えることができる

```Swift
var data = """
{
  "ID": 1,
  "Name": "Taro",
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name: String
    
    enum CodingKeys: String, CodingKey {
        case id = "ID"
        case name = "Name"
    }
}

let user: User = try JSONDecoder().decode(User.self, from: data)

print(user)
```

### データ構造に配列があるパターン

デコードメソッドの引数に配列で渡す

```Swift
var data = """
[
    {
        "id": 0,
        "name": "Mike"
    },
    {
        "id": 1,
        "name": "John"
    },
    {
        "id": 2,
        "name": "Mary"
    }
]
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name: String
}

let user: [User] = try JSONDecoder().decode([User].self, from: data)

print(user)
```

### ネストが深いパターン(自動デコード)

別のデコード用のstructを作る(あんまり自動で済むパターンないですが)

```Swift
var data = """
{
    "id": 0,
    "name": {
        "last": "Suzuki",
        "first": "Taro"
    }
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name:Name

    struct Name: Decodable {
        let last: String
        let first: String
    }
}

let user: User = try JSONDecoder().decode(User.self, from: data)
print(user)
```

### ネストが深いパターン(手動デコード)

おそらくこっちのがよくあるパターン 別のstructを作らずに済む
取得しない値(キー用意していない変数)は 辞書型なら`nestedContainer `、 配列なら`nestedUnkeyedContainer`で取得できる

階層ごとに別のCodingKeyを用意してあげる

```Swift
var data = """
{
    "id": 0,
    "name": {
        "last": "Suzuki",
        "first": "Taro"
    }
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let lastName: String
    let firstName: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
    }
    
    enum NameKeys: String, CodingKey {
        case lastName = "last"
        case firstName = "first"
    }
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(Int.self, forKey: .id)

        let nameValues = try values.nestedContainer(keyedBy: NameKeys.self, forKey: .name)
        lastName = try nameValues.decode(String.self, forKey: .lastName)
        firstName = try nameValues.decode(String.self, forKey: .firstName)
    }
}

let user: User = try JSONDecoder().decode(User.self, from: data)
print(user)
```

### キーが存在しない時があるパターン (自動デコード)

プロパティをオプショナル型にする

```Swift
var data = """
{
    "id": 0
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name: String?
}

let user: User = try JSONDecoder().decode(User.self, from: data)
print(user)
```

### キーが存在しない時があるパターン (手動デコード)

`decodeIfPresent`や`if let (変数) = try? decode~~` などを使って、もしキーが存在すればのデコードをする

```Swift
var data = """
{
    "id": 0
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
    }
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(Int.self, forKey: .id)
        name = try values.decodeIfPresent(String.self, forKey: .name) ?? ""
    }
}

let user: User = try JSONDecoder().decode(User.self, from: data)
print(user)
```

### 取得しない値が配列になっているパターン (手動デコード)

あんまりQiitaで見かけないのでこのパターンも記載(JSONデータの例が雑ですみません :pray:)

```Swift
var data = """
{
    "id": 0,
    "names": [
        {
            "name": "太郎(日本名)"
        },
        {
            "name": "テンプレマン(ニックネーム)"
        },
        {
            "name": "Taro(アメリカネーム)"
        }
    ]
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name: [String]
    
    enum CodingKeys: String, CodingKey {
        case id
        case names
    }
    
    enum NameKeys: String, CodingKey {
        case name
    }
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(Int.self, forKey: .id)
        
        var names = try values.nestedUnkeyedContainer(forKey: .names)
        var nameArray: [String] = []

        while !names.isAtEnd {
            let nameDic = try names.nestedContainer(keyedBy: NameKeys.self)
            let nameValue = try nameDic.decode(String.self, forKey: .name)
            nameArray.append(nameValue)
        }
        name = nameArray
    }

}

let user: User = try JSONDecoder().decode(User.self, from: data)
print(user)
```

### どっちかのキーで返ってくるパターン

EnumにDecodableを準拠させたプロパティを用意して、それを使って分岐で両方デコードする

```Swift
var data1 = """
{
    "id": 0,
    "name": "Taro"
}
""".data(using: .utf8)!

var data2 = """
{
    "id": 1,
    "name": {
        "last": "Suzuki",
        "first": "Ichiro"
    }
}
""".data(using: .utf8)!

struct User: Decodable {
    let id: Int
    let name: NameType
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
    }
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(Int.self, forKey: .id)
        name = try values.decode(NameType.self, forKey: .name)
    }
    
    enum NameType: Decodable {
        case firstNameOnly(String)
        case fullName(Name)
        
        init(from decoder: Decoder) throws {
            let nameType = try decoder.singleValueContainer()
            
            if let nameValue = try? nameType.decode(Name.self) {
                self = .fullName(nameValue)
            }
            else {
                let nameValue = try nameType.decode(String.self)
                self = .firstNameOnly(nameValue)
            }
        }
    }
}

struct Name: Decodable {
    let last: String
    let first: String
    
    private enum NameKeys: String, CodingKey {
        case last
        case first
    }
    
    init(from decoder: Decoder) throws {
        let nameValues = try decoder.container(keyedBy: NameKeys.self)
        last = try nameValues.decode(String.self, forKey: .last)
        first = try nameValues.decode(String.self, forKey: .first)
    }
}

let user1: User = try JSONDecoder().decode(User.self, from: data1)
print(user1)
let user2: User = try JSONDecoder().decode(User.self, from: data2)
print(user2)
```

### 感想
もし記載ミス等あればご指摘お願いします :bow: (JSONデータ例が雑なのは悪しからず。。)
パースって大変ですよね(白目)

### 追記

仕事時に参考にしていた記事
[【Swift】Codableについて備忘録
](https://qiita.com/s_emoto/items/deda5abcb0adc2217e86)
[レスポンスのJSONが異なるAPIにオプショナルを使わず対応する方法(Swift)](https://qiita.com/uhooi/items/5fefd61592eecc030844)
[Codableについて色々まとめた[Swift4.x]](https://qiita.com/_ha1f/items/bf1aad5ea3e927f59f9d)