デコード先の型を推論させたいという、ヒトなら誰しもが持つ欲望
@inamiy さんのスライド Swift 4 Codable // Speaker Deck をはじめとして、多くの人が叫んでいます。
Swift4のDecoder, DecodingContainerでは、明示的にデコード先の型を指定する必要があります。
これは熟慮の上での言語デザインだとのことです。
It was a deliberate design choice to make it clear what type we are decoding at any point. Sometimes type inference is too magical
— Doug Gregor (@dgregor79) 2017年6月11日
…分かりますよ。分かりますけど。
とはいえ我々は型推論させたい! という思いは捨てきれないわけです。
それ、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)
こんなん使うのって、どうなんでしょうねー。