LoginSignup
25
16

More than 5 years have passed since last update.

Decoder, DecodingContainerの、デコード先の型を推論させたい!明示的に指定したくない!

Last updated at Posted at 2017-06-29

デコード先の型を推論させたいという、ヒトなら誰しもが持つ欲望

@inamiy さんのスライド Swift 4 Codable // Speaker Deck をはじめとして、多くの人が叫んでいます。

image.png

Swift4のDecoder, DecodingContainerでは、明示的にデコード先の型を指定する必要があります。
これは熟慮の上での言語デザインだとのことです。

…分かりますよ。分かりますけど。
とはいえ我々は型推論させたい! という思いは捨てきれないわけです。

それ、extensionでできるよ。

Docoder+TypeInference.swift
import Foundation

extension JSONDecoder {
    func decode<T: Decodable>(from data: Data) throws -> T {
        return try decode(T.self, from: data)
    }
}
extension PropertyListDecoder {
    func decode<T: Decodable>(from data: Data) throws -> T {
        return try decode(T.self, from: data)
    }
}

extension KeyedDecodingContainerProtocol {
    func decode(forKey key: Self.Key) throws -> Bool { return try decode(Bool.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> Int { return try decode(Int.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> Int8 { return try decode(Int8.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> Int16 { return try decode(Int16.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> Int32 { return try decode(Int32.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> Int64 { return try decode(Int64.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> UInt { return try decode(UInt.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> UInt8 { return try decode(UInt8.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> UInt16 { return try decode(UInt16.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> UInt32 { return try decode(UInt32.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> UInt64 { return try decode(UInt64.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> Float { return try decode(Float.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> Double { return try decode(Double.self, forKey: key) }
    func decode(forKey key: Self.Key) throws -> String { return try decode(String.self, forKey: key) }
    func decode<T: Decodable>(forKey key: Self.Key) throws -> T { return try decode(T.self, forKey: key) }

    func decodeIfPresent(forKey key: Self.Key) throws -> Bool? { return try decodeIfPresent(Bool.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> Int? { return try decodeIfPresent(Int.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> Int8? { return try decodeIfPresent(Int8.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> Int16? { return try decodeIfPresent(Int16.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> Int32? { return try decodeIfPresent(Int32.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> Int64? { return try decodeIfPresent(Int64.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> UInt? { return try decodeIfPresent(UInt.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> UInt8? { return try decodeIfPresent(UInt8.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> UInt16? { return try decodeIfPresent(UInt16.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> UInt32? { return try decodeIfPresent(UInt32.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> UInt64? { return try decodeIfPresent(UInt64.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> Float? { return try decodeIfPresent(Float.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> Double? { return try decodeIfPresent(Double.self, forKey: key) }
    func decodeIfPresent(forKey key: Self.Key) throws -> String? { return try decodeIfPresent(String.self, forKey: key) }
    func decodeIfPresent<T: Decodable>(forKey key: Self.Key) throws -> T? { return try decodeIfPresent(T.self, forKey: key) }
}

extension UnkeyedDecodingContainer {

    mutating func decode() throws -> Bool { return try decode(Bool.self) }
    mutating func decode() throws -> Int { return try decode(Int.self) }
    mutating func decode() throws -> Int8 { return try decode(Int8.self) }
    mutating func decode() throws -> Int16 { return try decode(Int16.self) }
    mutating func decode() throws -> Int32 { return try decode(Int32.self) }
    mutating func decode() throws -> Int64 { return try decode(Int64.self) }
    mutating func decode() throws -> UInt { return try decode(UInt.self) }
    mutating func decode() throws -> UInt8 { return try decode(UInt8.self) }
    mutating func decode() throws -> UInt16 { return try decode(UInt16.self) }
    mutating func decode() throws -> UInt32 { return try decode(UInt32.self) }
    mutating func decode() throws -> UInt64 { return try decode(UInt64.self) }
    mutating func decode() throws -> Float { return try decode(Float.self) }
    mutating func decode() throws -> Double { return try decode(Double.self) }
    mutating func decode() throws -> String { return try decode(String.self) }
    mutating func decode<T: Decodable>() throws -> T { return try decode(T.self) }

    mutating func decodeIfPresent() throws -> Bool? { return try decodeIfPresent(Bool.self) }
    mutating func decodeIfPresent() throws -> Int? { return try decodeIfPresent(Int.self) }
    mutating func decodeIfPresent() throws -> Int8? { return try decodeIfPresent(Int8.self) }
    mutating func decodeIfPresent() throws -> Int16? { return try decodeIfPresent(Int16.self) }
    mutating func decodeIfPresent() throws -> Int32? { return try decodeIfPresent(Int32.self) }
    mutating func decodeIfPresent() throws -> Int64? { return try decodeIfPresent(Int64.self) }
    mutating func decodeIfPresent() throws -> UInt? { return try decodeIfPresent(UInt.self) }
    mutating func decodeIfPresent() throws -> UInt8? { return try decodeIfPresent(UInt8.self) }
    mutating func decodeIfPresent() throws -> UInt16? { return try decodeIfPresent(UInt16.self) }
    mutating func decodeIfPresent() throws -> UInt32? { return try decodeIfPresent(UInt32.self) }
    mutating func decodeIfPresent() throws -> UInt64? { return try decodeIfPresent(UInt64.self) }
    mutating func decodeIfPresent() throws -> Float? { return try decodeIfPresent(Float.self) }
    mutating func decodeIfPresent() throws -> Double? { return try decodeIfPresent(Double.self) }
    mutating func decodeIfPresent() throws -> String? { return try decodeIfPresent(String.self) }
    mutating func decodeIfPresent<T: Decodable>() throws -> T? { return try decodeIfPresent(T.self) }
}
extension SingleValueDecodingContainer {
    func decode() throws -> Bool { return try decode(Bool.self) }
    func decode() throws -> Int { return try decode(Int.self) }
    func decode() throws -> Int8 { return try decode(Int8.self) }
    func decode() throws -> Int16 { return try decode(Int16.self) }
    func decode() throws -> Int32 { return try decode(Int32.self) }
    func decode() throws -> Int64 { return try decode(Int64.self) }
    func decode() throws -> UInt { return try decode(UInt.self) }
    func decode() throws -> UInt8 { return try decode(UInt8.self) }
    func decode() throws -> UInt16 { return try decode(UInt16.self) }
    func decode() throws -> UInt32 { return try decode(UInt32.self) }
    func decode() throws -> UInt64 { return try decode(UInt64.self) }
    func decode() throws -> Float { return try decode(Float.self) }
    func decode() throws -> Double { return try decode(Double.self) }
    func decode() throws -> String { return try decode(String.self) }
    func decode<T: Decodable>() throws -> T { return try decode(T.self) }
}
struct Hoge: Decodable {

    let id: Int
    let text: String
    let url: URL
    let option: String?

    private enum CodingKeys: CodingKey {
        case id
        case text
        case url
        case option
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        // 普通は型を指定しなければいけないけど、
//        id = try container.decode(Int.self, forKey: .id)
//        text = try container.decode(String.self, forKey: .text)
//        url = try container.decode(URL.self, forKey: .url)
//        option = try container.decodeIfPresent(String.self, forKey: .option)

        // 理屈としては、型を省略しても推論可能なので、extensionを通して実現
        id = try container.decode(forKey: .id)
        text = try container.decode(forKey: .text)
        url = try container.decode(forKey: .url)
        option = try container.decodeIfPresent(forKey: .option)
    }
}

let data = """
{
    "id": 100,
    "text": "text",
    "url": "https://www.yahoo.co.jp",
    "option": null,
}
""".data(using: .utf8)!

let decoder = JSONDecoder()

//let h: Hoge = try! decoder.decode(Hoge.self, from: data) // 普通は引数として型を指定しなきゃいけないけど、
let h: Hoge = try! decoder.decode(from: data) // 理屈としては型を省略しても推論可能なので、extensionを通して実現

print(h)
// Hoge(id: 100, text: "text", url: https://www.yahoo.co.jp, option: nil)

こんなん使うのって、どうなんでしょうねー。

25
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
16