Last active
April 8, 2024 21:41
-
-
Save ollieatkinson/e05e0eb358b22b2038f50101c1c873e6 to your computer and use it in GitHub Desktop.
JSON Equatable in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public let null = NSNull() as AnyHashable | |
public enum JSONObject { | |
case array([Any]) | |
case dictionary([String: Any]) | |
case fragment(Any) | |
} | |
extension JSONObject { | |
@inlinable public static func with(_ data: Data, options: JSONSerialization.ReadingOptions = []) throws -> JSONObject { | |
try self.init(JSONSerialization.jsonObject(with: data, options: options)) | |
} | |
} | |
extension JSONObject { | |
@inlinable public func decode<T>(as type: T.Type = T.self, using decoder: JSONDecoder = .init()) throws -> T where T: Decodable { | |
try decoder.decode(type, from: JSONSerialization.data(withJSONObject: any, options: .fragmentsAllowed)) | |
} | |
@inlinable public func encode(options: JSONSerialization.WritingOptions = [.fragmentsAllowed]) throws -> Data { | |
try JSONSerialization.data(withJSONObject: any, options: options) | |
} | |
} | |
extension JSONObject { | |
public init(_ any: Any) { | |
switch any { | |
case let array as [Any]: self = .array(array) | |
case let dictionary as [String: Any]: self = .dictionary(dictionary) | |
case let fragment: self = .fragment(fragment) | |
} | |
} | |
public var any: Any { | |
switch self { | |
case let .array(o): return o | |
case let .dictionary(o): return o | |
case let .fragment(o): return o | |
} | |
} | |
} | |
extension JSONObject { | |
@inlinable public var dictionary: [String: Any]? { | |
guard case .dictionary(let dictionary) = self else { return nil } | |
return dictionary | |
} | |
@inlinable public var array: [Any]? { | |
guard case .array(let array) = self else { return nil } | |
return array | |
} | |
@inlinable public var fragment: Any? { | |
guard case .fragment(let fragment) = self else { return nil } | |
return fragment | |
} | |
} | |
extension JSONObject: CustomStringConvertible { | |
public var description: String { "\(any)" } | |
} | |
extension String: Error { } | |
extension JSONObject: CustomDebugStringConvertible { | |
public var debugDescription: String { | |
do { | |
guard let description = try String(data: encode(options: [.fragmentsAllowed, .prettyPrinted]), encoding: .utf8) else { | |
throw "Malformed UTF8" | |
} | |
return description | |
} catch { | |
return """ | |
🚫 Could not serialise object to json! | |
\(error) | |
""" | |
} | |
} | |
} | |
extension JSONObject { | |
public func isEqual<T>(to other: T) -> Bool where T: Equatable { | |
T.isEqual(any, other) | |
} | |
} | |
extension JSONObject { | |
public func isEqual(to any: Any) -> Bool { | |
switch self { | |
case let .array(o): | |
return [AnyHashable].isEqual(o, any) | |
case let .dictionary(o): | |
return [String: AnyHashable].isEqual(o, any) | |
case let .fragment(o): | |
return AnyHashable.isEqual(o, any) | |
} | |
} | |
public func isEqual(to json: JSONObject) -> Bool { | |
switch (self, json) { | |
case let (.array(o), .array(json)): | |
return [AnyHashable].isEqual(o, json) | |
case let (.dictionary(o), .dictionary(json)): | |
return [String: AnyHashable].isEqual(o, json) | |
case let (.fragment(o), .fragment(json)): | |
return AnyHashable.isEqual(o, json) | |
default: return false | |
} | |
} | |
} | |
extension Equatable { | |
public static func isEqual(_ l: Any, _ r: Any) -> Bool { | |
guard let l = l as? Self else { return false } | |
return isEqual(l, r) | |
} | |
public static func isEqual(_ l: Self, _ r: Any) -> Bool { | |
isEqual(r, l) | |
} | |
public static func isEqual(_ l: Any, _ r: Self) -> Bool { | |
guard let l = l as? Self else { return false } | |
return l == r | |
} | |
} | |
extension JSONObject: ExpressibleByArrayLiteral { | |
public init(arrayLiteral elements: AnyHashable...) { | |
self = .array(elements) | |
} | |
} | |
extension JSONObject: ExpressibleByDictionaryLiteral { | |
public init(dictionaryLiteral elements: (String, AnyHashable)...) { | |
self = .dictionary(Dictionary(uniqueKeysWithValues: elements)) | |
} | |
} | |
extension JSONObject: ExpressibleByStringLiteral { | |
public init(stringLiteral value: String) { | |
self = .fragment(value) | |
} | |
} | |
extension JSONObject: ExpressibleByFloatLiteral { | |
public init(floatLiteral value: Double) { | |
self = .fragment(value) | |
} | |
} | |
extension JSONObject: ExpressibleByNilLiteral { | |
public init(nilLiteral: ()) { | |
self = .fragment(null) | |
} | |
} | |
extension JSONObject: ExpressibleByIntegerLiteral { | |
public init(integerLiteral value: IntegerLiteralType) { | |
self = .fragment(value) | |
} | |
} | |
extension JSONObject: ExpressibleByBooleanLiteral { | |
public init(booleanLiteral value: Bool) { | |
self = .fragment(value) | |
} | |
} |
Author
ollieatkinson
commented
Oct 1, 2020
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment