AnyDecodable.swift (5900B)
1 #if canImport(Foundation) 2 import Foundation 3 #endif 4 5 /** 6 A type-erased `Decodable` value. 7 8 The `AnyDecodable` type forwards decoding responsibilities 9 to an underlying value, hiding its specific underlying type. 10 11 You can decode mixed-type values in dictionaries 12 and other collections that require `Decodable` conformance 13 by declaring their contained type to be `AnyDecodable`: 14 15 let json = """ 16 { 17 "boolean": true, 18 "integer": 42, 19 "double": 3.141592653589793, 20 "string": "string", 21 "array": [1, 2, 3], 22 "nested": { 23 "a": "alpha", 24 "b": "bravo", 25 "c": "charlie" 26 }, 27 "null": null 28 } 29 """.data(using: .utf8)! 30 31 let decoder = JSONDecoder() 32 let dictionary = try! decoder.decode([String: AnyDecodable].self, from: json) 33 */ 34 @frozen public struct AnyDecodable: Decodable { 35 public let value: Any 36 37 public init<T>(_ value: T?) { 38 self.value = value ?? () 39 } 40 } 41 42 @usableFromInline 43 protocol _AnyDecodable { 44 var value: Any { get } 45 init<T>(_ value: T?) 46 } 47 48 extension AnyDecodable: _AnyDecodable {} 49 50 extension _AnyDecodable { 51 public init(from decoder: Decoder) throws { 52 let container = try decoder.singleValueContainer() 53 54 if container.decodeNil() { 55 #if canImport(Foundation) 56 self.init(NSNull()) 57 #else 58 self.init(Optional<Self>.none) 59 #endif 60 } else if let bool = try? container.decode(Bool.self) { 61 self.init(bool) 62 } else if let int = try? container.decode(Int.self) { 63 self.init(int) 64 } else if let uint = try? container.decode(UInt.self) { 65 self.init(uint) 66 } else if let double = try? container.decode(Double.self) { 67 self.init(double) 68 } else if let string = try? container.decode(String.self) { 69 self.init(string) 70 } else if let array = try? container.decode([AnyDecodable].self) { 71 self.init(array.map { $0.value }) 72 } else if let dictionary = try? container.decode([String: AnyDecodable].self) { 73 self.init(dictionary.mapValues { $0.value }) 74 } else { 75 throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded") 76 } 77 } 78 } 79 80 extension AnyDecodable: Equatable { 81 public static func == (lhs: AnyDecodable, rhs: AnyDecodable) -> Bool { 82 switch (lhs.value, rhs.value) { 83 #if canImport(Foundation) 84 case is (NSNull, NSNull), is (Void, Void): 85 return true 86 #endif 87 case let (lhs as Bool, rhs as Bool): 88 return lhs == rhs 89 case let (lhs as Int, rhs as Int): 90 return lhs == rhs 91 case let (lhs as Int8, rhs as Int8): 92 return lhs == rhs 93 case let (lhs as Int16, rhs as Int16): 94 return lhs == rhs 95 case let (lhs as Int32, rhs as Int32): 96 return lhs == rhs 97 case let (lhs as Int64, rhs as Int64): 98 return lhs == rhs 99 case let (lhs as UInt, rhs as UInt): 100 return lhs == rhs 101 case let (lhs as UInt8, rhs as UInt8): 102 return lhs == rhs 103 case let (lhs as UInt16, rhs as UInt16): 104 return lhs == rhs 105 case let (lhs as UInt32, rhs as UInt32): 106 return lhs == rhs 107 case let (lhs as UInt64, rhs as UInt64): 108 return lhs == rhs 109 case let (lhs as Float, rhs as Float): 110 return lhs == rhs 111 case let (lhs as Double, rhs as Double): 112 return lhs == rhs 113 case let (lhs as String, rhs as String): 114 return lhs == rhs 115 case let (lhs as [String: AnyDecodable], rhs as [String: AnyDecodable]): 116 return lhs == rhs 117 case let (lhs as [AnyDecodable], rhs as [AnyDecodable]): 118 return lhs == rhs 119 default: 120 return false 121 } 122 } 123 } 124 125 extension AnyDecodable: CustomStringConvertible { 126 public var description: String { 127 switch value { 128 case is Void: 129 return String(describing: nil as Any?) 130 case let value as CustomStringConvertible: 131 return value.description 132 default: 133 return String(describing: value) 134 } 135 } 136 } 137 138 extension AnyDecodable: CustomDebugStringConvertible { 139 public var debugDescription: String { 140 switch value { 141 case let value as CustomDebugStringConvertible: 142 return "AnyDecodable(\(value.debugDescription))" 143 default: 144 return "AnyDecodable(\(description))" 145 } 146 } 147 } 148 149 extension AnyDecodable: Hashable { 150 public func hash(into hasher: inout Hasher) { 151 switch value { 152 case let value as Bool: 153 hasher.combine(value) 154 case let value as Int: 155 hasher.combine(value) 156 case let value as Int8: 157 hasher.combine(value) 158 case let value as Int16: 159 hasher.combine(value) 160 case let value as Int32: 161 hasher.combine(value) 162 case let value as Int64: 163 hasher.combine(value) 164 case let value as UInt: 165 hasher.combine(value) 166 case let value as UInt8: 167 hasher.combine(value) 168 case let value as UInt16: 169 hasher.combine(value) 170 case let value as UInt32: 171 hasher.combine(value) 172 case let value as UInt64: 173 hasher.combine(value) 174 case let value as Float: 175 hasher.combine(value) 176 case let value as Double: 177 hasher.combine(value) 178 case let value as String: 179 hasher.combine(value) 180 case let value as [String: AnyDecodable]: 181 hasher.combine(value) 182 case let value as [AnyDecodable]: 183 hasher.combine(value) 184 default: 185 break 186 } 187 } 188 }