AnyEncodable.swift (9284B)
1 #if canImport(Foundation) 2 import Foundation 3 #endif 4 5 /** 6 A type-erased `Encodable` value. 7 8 The `AnyEncodable` type forwards encoding responsibilities 9 to an underlying value, hiding its specific underlying type. 10 11 You can encode mixed-type values in dictionaries 12 and other collections that require `Encodable` conformance 13 by declaring their contained type to be `AnyEncodable`: 14 15 let dictionary: [String: AnyEncodable] = [ 16 "boolean": true, 17 "integer": 42, 18 "double": 3.141592653589793, 19 "string": "string", 20 "array": [1, 2, 3], 21 "nested": [ 22 "a": "alpha", 23 "b": "bravo", 24 "c": "charlie" 25 ], 26 "null": nil 27 ] 28 29 let encoder = JSONEncoder() 30 let json = try! encoder.encode(dictionary) 31 */ 32 @frozen public struct AnyEncodable: Encodable { 33 public let value: Any 34 35 public init<T>(_ value: T?) { 36 self.value = value ?? () 37 } 38 } 39 40 @usableFromInline 41 protocol _AnyEncodable { 42 var value: Any { get } 43 init<T>(_ value: T?) 44 } 45 46 extension AnyEncodable: _AnyEncodable {} 47 48 // MARK: - Encodable 49 50 extension _AnyEncodable { 51 public func encode(to encoder: Encoder) throws { 52 var container = encoder.singleValueContainer() 53 54 switch value { 55 #if canImport(Foundation) 56 case is NSNull: 57 try container.encodeNil() 58 #endif 59 case is Void: 60 try container.encodeNil() 61 case let bool as Bool: 62 try container.encode(bool) 63 case let int as Int: 64 try container.encode(int) 65 case let int8 as Int8: 66 try container.encode(int8) 67 case let int16 as Int16: 68 try container.encode(int16) 69 case let int32 as Int32: 70 try container.encode(int32) 71 case let int64 as Int64: 72 try container.encode(int64) 73 case let uint as UInt: 74 try container.encode(uint) 75 case let uint8 as UInt8: 76 try container.encode(uint8) 77 case let uint16 as UInt16: 78 try container.encode(uint16) 79 case let uint32 as UInt32: 80 try container.encode(uint32) 81 case let uint64 as UInt64: 82 try container.encode(uint64) 83 case let float as Float: 84 try container.encode(float) 85 case let double as Double: 86 try container.encode(double) 87 case let string as String: 88 try container.encode(string) 89 #if canImport(Foundation) 90 case let number as NSNumber: 91 try encode(nsnumber: number, into: &container) 92 case let date as Date: 93 try container.encode(date) 94 case let profile_url as URL: 95 try container.encode(profile_url) 96 #endif 97 case let array as [Any?]: 98 try container.encode(array.map { AnyEncodable($0) }) 99 case let dictionary as [String: Any?]: 100 try container.encode(dictionary.mapValues { AnyEncodable($0) }) 101 case let encodable as Encodable: 102 try encodable.encode(to: encoder) 103 default: 104 let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded") 105 throw EncodingError.invalidValue(value, context) 106 } 107 } 108 109 #if canImport(Foundation) 110 private func encode(nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws { 111 switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) { 112 case "B": 113 try container.encode(nsnumber.boolValue) 114 case "c": 115 try container.encode(nsnumber.int8Value) 116 case "s": 117 try container.encode(nsnumber.int16Value) 118 case "i", "l": 119 try container.encode(nsnumber.int32Value) 120 case "q": 121 try container.encode(nsnumber.int64Value) 122 case "C": 123 try container.encode(nsnumber.uint8Value) 124 case "S": 125 try container.encode(nsnumber.uint16Value) 126 case "I", "L": 127 try container.encode(nsnumber.uint32Value) 128 case "Q": 129 try container.encode(nsnumber.uint64Value) 130 case "f": 131 try container.encode(nsnumber.floatValue) 132 case "d": 133 try container.encode(nsnumber.doubleValue) 134 default: 135 let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "NSNumber cannot be encoded because its type is not handled") 136 throw EncodingError.invalidValue(nsnumber, context) 137 } 138 } 139 #endif 140 } 141 142 extension AnyEncodable: Equatable { 143 public static func == (lhs: AnyEncodable, rhs: AnyEncodable) -> Bool { 144 switch (lhs.value, rhs.value) { 145 case is (Void, Void): 146 return true 147 case let (lhs as Bool, rhs as Bool): 148 return lhs == rhs 149 case let (lhs as Int, rhs as Int): 150 return lhs == rhs 151 case let (lhs as Int8, rhs as Int8): 152 return lhs == rhs 153 case let (lhs as Int16, rhs as Int16): 154 return lhs == rhs 155 case let (lhs as Int32, rhs as Int32): 156 return lhs == rhs 157 case let (lhs as Int64, rhs as Int64): 158 return lhs == rhs 159 case let (lhs as UInt, rhs as UInt): 160 return lhs == rhs 161 case let (lhs as UInt8, rhs as UInt8): 162 return lhs == rhs 163 case let (lhs as UInt16, rhs as UInt16): 164 return lhs == rhs 165 case let (lhs as UInt32, rhs as UInt32): 166 return lhs == rhs 167 case let (lhs as UInt64, rhs as UInt64): 168 return lhs == rhs 169 case let (lhs as Float, rhs as Float): 170 return lhs == rhs 171 case let (lhs as Double, rhs as Double): 172 return lhs == rhs 173 case let (lhs as String, rhs as String): 174 return lhs == rhs 175 case let (lhs as [String: AnyEncodable], rhs as [String: AnyEncodable]): 176 return lhs == rhs 177 case let (lhs as [AnyEncodable], rhs as [AnyEncodable]): 178 return lhs == rhs 179 default: 180 return false 181 } 182 } 183 } 184 185 extension AnyEncodable: CustomStringConvertible { 186 public var description: String { 187 switch value { 188 case is Void: 189 return String(describing: nil as Any?) 190 case let value as CustomStringConvertible: 191 return value.description 192 default: 193 return String(describing: value) 194 } 195 } 196 } 197 198 extension AnyEncodable: CustomDebugStringConvertible { 199 public var debugDescription: String { 200 switch value { 201 case let value as CustomDebugStringConvertible: 202 return "AnyEncodable(\(value.debugDescription))" 203 default: 204 return "AnyEncodable(\(description))" 205 } 206 } 207 } 208 209 extension AnyEncodable: ExpressibleByNilLiteral {} 210 extension AnyEncodable: ExpressibleByBooleanLiteral {} 211 extension AnyEncodable: ExpressibleByIntegerLiteral {} 212 extension AnyEncodable: ExpressibleByFloatLiteral {} 213 extension AnyEncodable: ExpressibleByStringLiteral {} 214 extension AnyEncodable: ExpressibleByStringInterpolation {} 215 extension AnyEncodable: ExpressibleByArrayLiteral {} 216 extension AnyEncodable: ExpressibleByDictionaryLiteral {} 217 218 extension _AnyEncodable { 219 public init(nilLiteral _: ()) { 220 self.init(nil as Any?) 221 } 222 223 public init(booleanLiteral value: Bool) { 224 self.init(value) 225 } 226 227 public init(integerLiteral value: Int) { 228 self.init(value) 229 } 230 231 public init(floatLiteral value: Double) { 232 self.init(value) 233 } 234 235 public init(extendedGraphemeClusterLiteral value: String) { 236 self.init(value) 237 } 238 239 public init(stringLiteral value: String) { 240 self.init(value) 241 } 242 243 public init(arrayLiteral elements: Any...) { 244 self.init(elements) 245 } 246 247 public init(dictionaryLiteral elements: (AnyHashable, Any)...) { 248 self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first })) 249 } 250 } 251 252 extension AnyEncodable: Hashable { 253 public func hash(into hasher: inout Hasher) { 254 switch value { 255 case let value as Bool: 256 hasher.combine(value) 257 case let value as Int: 258 hasher.combine(value) 259 case let value as Int8: 260 hasher.combine(value) 261 case let value as Int16: 262 hasher.combine(value) 263 case let value as Int32: 264 hasher.combine(value) 265 case let value as Int64: 266 hasher.combine(value) 267 case let value as UInt: 268 hasher.combine(value) 269 case let value as UInt8: 270 hasher.combine(value) 271 case let value as UInt16: 272 hasher.combine(value) 273 case let value as UInt32: 274 hasher.combine(value) 275 case let value as UInt64: 276 hasher.combine(value) 277 case let value as Float: 278 hasher.combine(value) 279 case let value as Double: 280 hasher.combine(value) 281 case let value as String: 282 hasher.combine(value) 283 case let value as [String: AnyEncodable]: 284 hasher.combine(value) 285 case let value as [AnyEncodable]: 286 hasher.combine(value) 287 default: 288 break 289 } 290 } 291 }