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

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

@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)

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


『 Swift 』Article List