AnyCodable.swift 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /**
  2. * AnyCodable
  3. * Copyright (c) Morten Bjerg Gregersen 2020
  4. * MIT license, see LICENSE file for details
  5. */
  6. import Foundation
  7. /**
  8. A type-erased `Encodable` value.
  9. Heavily inspired by: https://github.com/Flight-School/AnyCodable
  10. The above can't be used as a dependency, as it triggers linker errors
  11. when Fluxor and FluxorTestSupport is used in a test target.
  12. */
  13. public struct AnyCodable {
  14. public let value: Any
  15. public init<T>(_ value: T?) {
  16. if let dictionary = value as? [String: AnyCodable] {
  17. self.value = dictionary as [AnyHashable: AnyCodable]
  18. } else if let dictionary = value as? [String: Any] {
  19. self.value = dictionary.mapValues(AnyCodable.init) as [AnyHashable: AnyCodable]
  20. } else if let array = value as? [Any], !(array is [AnyCodable]) {
  21. self.value = array.map(AnyCodable.init)
  22. } else {
  23. self.value = value ?? ()
  24. }
  25. }
  26. }
  27. extension AnyCodable: Encodable {
  28. // swiftlint:disable:next cyclomatic_complexity function_body_length
  29. public func encode(to encoder: Encoder) throws {
  30. var container = encoder.singleValueContainer()
  31. switch value {
  32. case is Void:
  33. try container.encodeNil()
  34. case let bool as Bool:
  35. try container.encode(bool)
  36. case let int as Int:
  37. try container.encode(int)
  38. case let int8 as Int8:
  39. try container.encode(int8)
  40. case let int16 as Int16:
  41. try container.encode(int16)
  42. case let int32 as Int32:
  43. try container.encode(int32)
  44. case let int64 as Int64:
  45. try container.encode(int64)
  46. case let uint as UInt:
  47. try container.encode(uint)
  48. case let uint8 as UInt8:
  49. try container.encode(uint8)
  50. case let uint16 as UInt16:
  51. try container.encode(uint16)
  52. case let uint32 as UInt32:
  53. try container.encode(uint32)
  54. case let uint64 as UInt64:
  55. try container.encode(uint64)
  56. case let float as Float:
  57. try container.encode(float)
  58. case let double as Double:
  59. try container.encode(double)
  60. case let string as String:
  61. try container.encode(string)
  62. case let date as Date:
  63. try container.encode(date)
  64. case let url as URL:
  65. try container.encode(url)
  66. case let array as [Any?]:
  67. try container.encode(array.map { AnyCodable($0) })
  68. case let dictionary as [String: Any?]:
  69. try container.encode(dictionary.mapValues { AnyCodable($0) })
  70. case let encodable as Encodable:
  71. try encodable.encode(to: encoder)
  72. default:
  73. let debugDescription = "Value cannot be encoded"
  74. let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: debugDescription)
  75. throw EncodingError.invalidValue(value, context)
  76. }
  77. }
  78. }
  79. extension AnyCodable: Decodable {
  80. public init(from decoder: Decoder) throws {
  81. let container = try decoder.singleValueContainer()
  82. if container.decodeNil() {
  83. self.init(())
  84. } else if let bool = try? container.decode(Bool.self) {
  85. self.init(bool)
  86. } else if let int = try? container.decode(Int.self) {
  87. self.init(int)
  88. } else if let double = try? container.decode(Double.self) {
  89. self.init(double)
  90. } else if let string = try? container.decode(String.self) {
  91. self.init(string)
  92. } else if let array = try? container.decode([AnyCodable].self) {
  93. self.init(array)
  94. } else if let dictionary = try? container.decode([String: AnyCodable].self) {
  95. self.init(dictionary)
  96. } else {
  97. throw DecodingError.dataCorruptedError(in: container, debugDescription: "Value cannot be decoded")
  98. }
  99. }
  100. }
  101. extension AnyCodable: Equatable {
  102. // swiftlint:disable:next cyclomatic_complexity
  103. public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool {
  104. switch (lhs.value, rhs.value) {
  105. case is (Void, Void):
  106. return true
  107. case let (lhs as Bool, rhs as Bool):
  108. return lhs == rhs
  109. case let (lhs as Int, rhs as Int):
  110. return lhs == rhs
  111. case let (lhs as Int8, rhs as Int8):
  112. return lhs == rhs
  113. case let (lhs as Int16, rhs as Int16):
  114. return lhs == rhs
  115. case let (lhs as Int32, rhs as Int32):
  116. return lhs == rhs
  117. case let (lhs as Int64, rhs as Int64):
  118. return lhs == rhs
  119. case let (lhs as UInt, rhs as UInt):
  120. return lhs == rhs
  121. case let (lhs as UInt8, rhs as UInt8):
  122. return lhs == rhs
  123. case let (lhs as UInt16, rhs as UInt16):
  124. return lhs == rhs
  125. case let (lhs as UInt32, rhs as UInt32):
  126. return lhs == rhs
  127. case let (lhs as UInt64, rhs as UInt64):
  128. return lhs == rhs
  129. case let (lhs as Float, rhs as Float):
  130. return lhs == rhs
  131. case let (lhs as Double, rhs as Double):
  132. return lhs == rhs
  133. case let (lhs as String, rhs as String):
  134. return lhs == rhs
  135. case let (lhs as [AnyHashable: AnyCodable], rhs as [AnyHashable: AnyCodable]):
  136. return lhs == rhs
  137. case let (lhs as [AnyCodable], rhs as [AnyCodable]):
  138. return lhs == rhs
  139. default:
  140. return false
  141. }
  142. }
  143. }
  144. extension AnyCodable: CustomStringConvertible {
  145. public var description: String {
  146. switch value {
  147. case is Void:
  148. return String(describing: nil as Any?)
  149. case let value as CustomStringConvertible:
  150. return value.description
  151. default:
  152. return String(describing: value)
  153. }
  154. }
  155. }
  156. extension AnyCodable: CustomDebugStringConvertible {
  157. public var debugDescription: String {
  158. switch value {
  159. case let value as CustomDebugStringConvertible:
  160. return "AnyCodable(\(value.debugDescription))"
  161. default:
  162. return "AnyCodable(\(description))"
  163. }
  164. }
  165. }
  166. extension AnyCodable: ExpressibleByNilLiteral {}
  167. extension AnyCodable: ExpressibleByBooleanLiteral {}
  168. extension AnyCodable: ExpressibleByIntegerLiteral {}
  169. extension AnyCodable: ExpressibleByFloatLiteral {}
  170. extension AnyCodable: ExpressibleByExtendedGraphemeClusterLiteral {}
  171. extension AnyCodable: ExpressibleByStringLiteral {}
  172. extension AnyCodable: ExpressibleByArrayLiteral {}
  173. extension AnyCodable: ExpressibleByDictionaryLiteral {}
  174. public extension AnyCodable {
  175. init(nilLiteral _: ()) {
  176. self.init(nil as Any?)
  177. }
  178. init(booleanLiteral value: Bool) {
  179. self.init(value)
  180. }
  181. init(integerLiteral value: Int) {
  182. self.init(value)
  183. }
  184. init(floatLiteral value: Double) {
  185. self.init(value)
  186. }
  187. init(stringLiteral value: String) {
  188. self.init(value)
  189. }
  190. init(arrayLiteral elements: Any...) {
  191. self.init(elements)
  192. }
  193. init(dictionaryLiteral elements: (AnyHashable, Any)...) {
  194. self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first }))
  195. }
  196. }