Codableで異なる日付フォーマットを含むデータをデコードする

通常，Swift4のCodableで日付の情報を含むデータを扱う場合， `JSONDecoder.dateDecodingStrategy`を指定することでデコードできます．

ただ，この方法では異なる日付フォーマットのデータがある場合はパースできません．
いささかニッチな状況ではありますが，色々試した結果以下のやり方に落ち着きました．

```Swift
struct MultipleDateFormatModel: Codable {
    let created: Date        // ISO8601形式
    let publishedDate: Date  // "yyyy-MM-dd"形式
    
    private enum CodingKeys: String, CodingKey {
        case created
        case publishedDate
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // JSONDecoder.dateDecodingStrategyで想定される形式のデータはそのままDateとしてデコード
        created = try container.decode(Date.self, forKey: .created)
        
        // それ以外の形式のデータは一旦Stringとしてデコードし，Dateに変換
        let pubishedDateString = try container.decode(String.self, forKey: .publishedDate)
        let formatter = MultipleDateFormatModel.yyyymmddFormatter
        if let date = formatter.date(from: pubishedDateString) {
            publishedDate = date
        } else {
            throw DecodingError.dataCorruptedError(forKey: .publishedDate,
                                                   in: container,
                                                   debugDescription: "Unexpected date format")
        }
    }
    
    static var yyyymmddFormatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
    
        return formatter
    }
}
```

ポイントは`JSONDecoder.dateDecodingStrategy`で想定されるものと異なるフォーマットのデータについては，
`Date`として扱わずにいったん`String`にデコードしたのち，`DateFormatter`を使って`Date`型に変換する，ということです．


デコード結果は以下のようになります．

```Swift
let acceptableData = """
{
"created": "2018-03-01T12:34:56+0900",
"publishedDate": "2018-02-01"
}
""".data(using: .utf8)!

// publishedDateが"yyyy-MM-dd"形式ではないためデコードできない
let unacceptableData = """
{
"created": "2018-03-01T12:34:56+0900",
"publishedDate": "2018-03-01T12:34:56+0900"
}
""".data(using: .utf8)!


print("=====Decode acceptableData")
decodeAndPrint(data: acceptableData)

print("=====Decode unacceptableData")
decodeAndPrint(data: unacceptableData)


func decodeAndPrint(data: Data) {
    let decoder: JSONDecoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    do {
        let decoded = try decoder.decode(MultipleDateFormatModel.self, from: data)
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .short
        print("publishedDate: \(formatter.string(from: decoded.publishedDate))")
        print("publishedDate(RAW): \(decoded.publishedDate)")
    } catch let error {
        print(error)
    }
}

/* Result
=====Decode acceptableData
created: 3/1/18, 12:34 PM
publishedDate: 2/1/18, 12:00 AM
=====Decode unacceptableData
dataCorrupted(Swift.DecodingError.Context(codingPath: [__lldb_expr_130.MultipleDateFormatModel.(CodingKeys in _0FD4C678636198E02AFD58B14D3269B2).publishedDate], debugDescription: "Unexpected date format", underlyingError: nil))
*/
```

いまいちCodableの恩恵を受け切れていないようなモヤモヤ感やら，
そもそもそのデータ設計はどうなんだというツッコミはありますが，記録として．


#### 参考
[Swift4のJSONDecorderは、Date等のパース方法をカスタマイズできるみたい](https://qiita.com/takasek/items/b2b0e3849e204037d2de)
[Swift4のCodableでISO8601の日付をデコードする](https://qiita.com/rizumita/items/88c38f5671e0627c5df6)
[Swift Codable With Custom Dates](https://useyourloaf.com/blog/swift-codable-with-custom-dates/)