commit d559dd3a139bf5d02c63a3f5602b8a05ab094726
parent b9c2473a2dd56616f0605964c0eef9e19c6570d9
Author: William Casarin <jb55@jb55.com>
Date: Fri, 27 Jan 2023 10:16:56 -0800
Allow non-string values in profiles
Profiles were not loading from other clients because some fields were
not strings
Changelog-Fixed: Fixed profiles sometimes not loading from other clients
Diffstat:
5 files changed, 691 insertions(+), 24 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -166,6 +166,9 @@
4CF0ABE12981A83900D66079 /* MutelistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE02981A83900D66079 /* MutelistView.swift */; };
4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE22981BC7D00D66079 /* UserView.swift */; };
4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE42981EE0C00D66079 /* EULAView.swift */; };
+ 4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE829844AF100D66079 /* AnyCodable.swift */; };
+ 4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */; };
+ 4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; };
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
@@ -419,6 +422,9 @@
4CF0ABE02981A83900D66079 /* MutelistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutelistView.swift; sourceTree = "<group>"; };
4CF0ABE22981BC7D00D66079 /* UserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserView.swift; sourceTree = "<group>"; };
4CF0ABE42981EE0C00D66079 /* EULAView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EULAView.swift; sourceTree = "<group>"; };
+ 4CF0ABE829844AF100D66079 /* AnyCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = "<group>"; };
+ 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = "<group>"; };
+ 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; };
647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; };
@@ -662,6 +668,7 @@
4C7FF7D628233637009601DB /* Util */ = {
isa = PBXGroup;
children = (
+ 4CF0ABEA29844B2F00D66079 /* AnyCodable */,
4C3A1D322960DB0500558C0F /* Markdown.swift */,
4CEE2AF4280B29E600AB5EEF /* TimeAgo.swift */,
4CE4F8CC281352B30009DFBB /* Notifications.swift */,
@@ -824,6 +831,16 @@
path = Muting;
sourceTree = "<group>";
};
+ 4CF0ABEA29844B2F00D66079 /* AnyCodable */ = {
+ isa = PBXGroup;
+ children = (
+ 4CF0ABE829844AF100D66079 /* AnyCodable.swift */,
+ 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */,
+ 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */,
+ );
+ path = AnyCodable;
+ sourceTree = "<group>";
+ };
F7F0BA23297892AE009531F3 /* Modifiers */ = {
isa = PBXGroup;
children = (
@@ -1003,6 +1020,7 @@
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
+ 4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */,
4CB8838D296F710400DC99E7 /* Reposted.swift in Sources */,
4C3EA67728FF7A9800C48A62 /* talstr.c in Sources */,
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
@@ -1053,6 +1071,7 @@
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */,
7C45AE71297353390031D7BC /* KFImageModel.swift in Sources */,
+ 4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */,
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */,
4CC7AAF2297F129C00430951 /* EmbeddedEventView.swift in Sources */,
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */,
@@ -1122,6 +1141,7 @@
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
+ 4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */,
4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */,
diff --git a/damus/Nostr/Nostr.swift b/damus/Nostr/Nostr.swift
@@ -8,7 +8,7 @@
import Foundation
struct Profile: Codable {
- var value: [String: String]
+ var value: [String: AnyCodable]
init (name: String?, display_name: String?, about: String?, picture: String?, banner: String?, website: String?, lud06: String?, lud16: String?, nip05: String?) {
self.value = [:]
@@ -23,48 +23,69 @@ struct Profile: Codable {
self.nip05 = nip05
}
+ private func str(_ str: String) -> String? {
+ guard let val = self.value[str] else{
+ return nil
+ }
+
+ guard let s = val.value as? String else {
+ return nil
+ }
+
+ return s
+ }
+
+ private mutating func set_str(_ key: String, _ val: String?) {
+ if val == nil {
+ self.value.removeValue(forKey: key)
+ return
+ }
+
+ self.value[key] = AnyCodable.init(val)
+ }
+
var display_name: String? {
- get { return value["display_name"]; }
- set(s) { value["display_name"] = s }
+ get { return str("display_name"); }
+ set(s) { set_str("display_name", s) }
}
var name: String? {
- get { return value["name"]; }
- set(s) { value["name"] = s }
+ get { return str("name"); }
+ set(s) { set_str("name", s) }
}
var about: String? {
- get { return value["about"]; }
- set(s) { value["about"] = s }
+ get { return str("about"); }
+ set(s) { set_str("about", s) }
}
var picture: String? {
- get { return value["picture"]; }
- set(s) { value["picture"] = s }
+ get { return str("picture"); }
+ set(s) { set_str("picture", s) }
}
var banner: String? {
- get { return value["banner"]; }
- set(s) { value["banner"] = s }
+ get { return str("banner"); }
+ set(s) { set_str("banner", s) }
}
var website: String? {
- get { return value["website"]; }
- set(s) { value["website"] = s }
- }
-
- var website_url: URL? {
- return self.website.flatMap { URL(string: $0) }
+ get { return str("website"); }
+ set(s) { set_str("website", s) }
}
var lud06: String? {
- get { return value["lud06"]; }
- set(s) { value["lud06"] = s }
+ get { return str("lud06"); }
+ set(s) { set_str("lud06", s) }
}
var lud16: String? {
- get { return value["lud16"]; }
- set(s) { value["lud16"] = s }
+ get { return str("lud16"); }
+ set(s) { set_str("lud16", s) }
+ }
+
+ var website_url: URL? {
+ return self.website.flatMap { URL(string: $0) }
}
var lnurl: String? {
@@ -80,8 +101,8 @@ struct Profile: Codable {
}
var nip05: String? {
- get { return value["nip05"]; }
- set(s) { value["nip05"] = s }
+ get { return str("nip05"); }
+ set(s) { set_str("nip05", s) }
}
var lightning_uri: URL? {
@@ -90,7 +111,7 @@ struct Profile: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
- self.value = try container.decode([String: String].self)
+ self.value = try container.decode([String: AnyCodable].self)
}
func encode(to encoder: Encoder) throws {
diff --git a/damus/Util/AnyCodable/AnyCodable.swift b/damus/Util/AnyCodable/AnyCodable.swift
@@ -0,0 +1,147 @@
+import Foundation
+/**
+ A type-erased `Codable` value.
+
+ The `AnyCodable` type forwards encoding and decoding responsibilities
+ to an underlying value, hiding its specific underlying type.
+
+ You can encode or decode mixed-type values in dictionaries
+ and other collections that require `Encodable` or `Decodable` conformance
+ by declaring their contained type to be `AnyCodable`.
+
+ - SeeAlso: `AnyEncodable`
+ - SeeAlso: `AnyDecodable`
+ */
+@frozen public struct AnyCodable: Codable {
+ public let value: Any
+
+ public init<T>(_ value: T?) {
+ self.value = value ?? ()
+ }
+}
+
+extension AnyCodable: _AnyEncodable, _AnyDecodable {}
+
+extension AnyCodable: Equatable {
+ public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool {
+ switch (lhs.value, rhs.value) {
+ case is (Void, Void):
+ return true
+ case let (lhs as Bool, rhs as Bool):
+ return lhs == rhs
+ case let (lhs as Int, rhs as Int):
+ return lhs == rhs
+ case let (lhs as Int8, rhs as Int8):
+ return lhs == rhs
+ case let (lhs as Int16, rhs as Int16):
+ return lhs == rhs
+ case let (lhs as Int32, rhs as Int32):
+ return lhs == rhs
+ case let (lhs as Int64, rhs as Int64):
+ return lhs == rhs
+ case let (lhs as UInt, rhs as UInt):
+ return lhs == rhs
+ case let (lhs as UInt8, rhs as UInt8):
+ return lhs == rhs
+ case let (lhs as UInt16, rhs as UInt16):
+ return lhs == rhs
+ case let (lhs as UInt32, rhs as UInt32):
+ return lhs == rhs
+ case let (lhs as UInt64, rhs as UInt64):
+ return lhs == rhs
+ case let (lhs as Float, rhs as Float):
+ return lhs == rhs
+ case let (lhs as Double, rhs as Double):
+ return lhs == rhs
+ case let (lhs as String, rhs as String):
+ return lhs == rhs
+ case let (lhs as [String: AnyCodable], rhs as [String: AnyCodable]):
+ return lhs == rhs
+ case let (lhs as [AnyCodable], rhs as [AnyCodable]):
+ return lhs == rhs
+ case let (lhs as [String: Any], rhs as [String: Any]):
+ return NSDictionary(dictionary: lhs) == NSDictionary(dictionary: rhs)
+ case let (lhs as [Any], rhs as [Any]):
+ return NSArray(array: lhs) == NSArray(array: rhs)
+ case is (NSNull, NSNull):
+ return true
+ default:
+ return false
+ }
+ }
+}
+
+extension AnyCodable: CustomStringConvertible {
+ public var description: String {
+ switch value {
+ case is Void:
+ return String(describing: nil as Any?)
+ case let value as CustomStringConvertible:
+ return value.description
+ default:
+ return String(describing: value)
+ }
+ }
+}
+
+extension AnyCodable: CustomDebugStringConvertible {
+ public var debugDescription: String {
+ switch value {
+ case let value as CustomDebugStringConvertible:
+ return "AnyCodable(\(value.debugDescription))"
+ default:
+ return "AnyCodable(\(description))"
+ }
+ }
+}
+
+extension AnyCodable: ExpressibleByNilLiteral {}
+extension AnyCodable: ExpressibleByBooleanLiteral {}
+extension AnyCodable: ExpressibleByIntegerLiteral {}
+extension AnyCodable: ExpressibleByFloatLiteral {}
+extension AnyCodable: ExpressibleByStringLiteral {}
+extension AnyCodable: ExpressibleByStringInterpolation {}
+extension AnyCodable: ExpressibleByArrayLiteral {}
+extension AnyCodable: ExpressibleByDictionaryLiteral {}
+
+
+extension AnyCodable: Hashable {
+ public func hash(into hasher: inout Hasher) {
+ switch value {
+ case let value as Bool:
+ hasher.combine(value)
+ case let value as Int:
+ hasher.combine(value)
+ case let value as Int8:
+ hasher.combine(value)
+ case let value as Int16:
+ hasher.combine(value)
+ case let value as Int32:
+ hasher.combine(value)
+ case let value as Int64:
+ hasher.combine(value)
+ case let value as UInt:
+ hasher.combine(value)
+ case let value as UInt8:
+ hasher.combine(value)
+ case let value as UInt16:
+ hasher.combine(value)
+ case let value as UInt32:
+ hasher.combine(value)
+ case let value as UInt64:
+ hasher.combine(value)
+ case let value as Float:
+ hasher.combine(value)
+ case let value as Double:
+ hasher.combine(value)
+ case let value as String:
+ hasher.combine(value)
+ case let value as [String: AnyCodable]:
+ hasher.combine(value)
+ case let value as [AnyCodable]:
+ hasher.combine(value)
+ default:
+ break
+ }
+ }
+}
diff --git a/damus/Util/AnyCodable/AnyDecodable.swift b/damus/Util/AnyCodable/AnyDecodable.swift
@@ -0,0 +1,188 @@
+#if canImport(Foundation)
+import Foundation
+#endif
+
+/**
+ A type-erased `Decodable` value.
+
+ The `AnyDecodable` type forwards decoding responsibilities
+ to an underlying value, hiding its specific underlying type.
+
+ You can decode mixed-type values in dictionaries
+ and other collections that require `Decodable` conformance
+ by declaring their contained type to be `AnyDecodable`:
+
+ let json = """
+ {
+ "boolean": true,
+ "integer": 42,
+ "double": 3.141592653589793,
+ "string": "string",
+ "array": [1, 2, 3],
+ "nested": {
+ "a": "alpha",
+ "b": "bravo",
+ "c": "charlie"
+ },
+ "null": null
+ }
+ """.data(using: .utf8)!
+
+ let decoder = JSONDecoder()
+ let dictionary = try! decoder.decode([String: AnyDecodable].self, from: json)
+ */
+@frozen public struct AnyDecodable: Decodable {
+ public let value: Any
+
+ public init<T>(_ value: T?) {
+ self.value = value ?? ()
+ }
+}
+
+@usableFromInline
+protocol _AnyDecodable {
+ var value: Any { get }
+ init<T>(_ value: T?)
+}
+
+extension AnyDecodable: _AnyDecodable {}
+
+extension _AnyDecodable {
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+
+ if container.decodeNil() {
+ #if canImport(Foundation)
+ self.init(NSNull())
+ #else
+ self.init(Optional<Self>.none)
+ #endif
+ } else if let bool = try? container.decode(Bool.self) {
+ self.init(bool)
+ } else if let int = try? container.decode(Int.self) {
+ self.init(int)
+ } else if let uint = try? container.decode(UInt.self) {
+ self.init(uint)
+ } else if let double = try? container.decode(Double.self) {
+ self.init(double)
+ } else if let string = try? container.decode(String.self) {
+ self.init(string)
+ } else if let array = try? container.decode([AnyDecodable].self) {
+ self.init(array.map { $0.value })
+ } else if let dictionary = try? container.decode([String: AnyDecodable].self) {
+ self.init(dictionary.mapValues { $0.value })
+ } else {
+ throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded")
+ }
+ }
+}
+
+extension AnyDecodable: Equatable {
+ public static func == (lhs: AnyDecodable, rhs: AnyDecodable) -> Bool {
+ switch (lhs.value, rhs.value) {
+#if canImport(Foundation)
+ case is (NSNull, NSNull), is (Void, Void):
+ return true
+#endif
+ case let (lhs as Bool, rhs as Bool):
+ return lhs == rhs
+ case let (lhs as Int, rhs as Int):
+ return lhs == rhs
+ case let (lhs as Int8, rhs as Int8):
+ return lhs == rhs
+ case let (lhs as Int16, rhs as Int16):
+ return lhs == rhs
+ case let (lhs as Int32, rhs as Int32):
+ return lhs == rhs
+ case let (lhs as Int64, rhs as Int64):
+ return lhs == rhs
+ case let (lhs as UInt, rhs as UInt):
+ return lhs == rhs
+ case let (lhs as UInt8, rhs as UInt8):
+ return lhs == rhs
+ case let (lhs as UInt16, rhs as UInt16):
+ return lhs == rhs
+ case let (lhs as UInt32, rhs as UInt32):
+ return lhs == rhs
+ case let (lhs as UInt64, rhs as UInt64):
+ return lhs == rhs
+ case let (lhs as Float, rhs as Float):
+ return lhs == rhs
+ case let (lhs as Double, rhs as Double):
+ return lhs == rhs
+ case let (lhs as String, rhs as String):
+ return lhs == rhs
+ case let (lhs as [String: AnyDecodable], rhs as [String: AnyDecodable]):
+ return lhs == rhs
+ case let (lhs as [AnyDecodable], rhs as [AnyDecodable]):
+ return lhs == rhs
+ default:
+ return false
+ }
+ }
+}
+
+extension AnyDecodable: CustomStringConvertible {
+ public var description: String {
+ switch value {
+ case is Void:
+ return String(describing: nil as Any?)
+ case let value as CustomStringConvertible:
+ return value.description
+ default:
+ return String(describing: value)
+ }
+ }
+}
+
+extension AnyDecodable: CustomDebugStringConvertible {
+ public var debugDescription: String {
+ switch value {
+ case let value as CustomDebugStringConvertible:
+ return "AnyDecodable(\(value.debugDescription))"
+ default:
+ return "AnyDecodable(\(description))"
+ }
+ }
+}
+
+extension AnyDecodable: Hashable {
+ public func hash(into hasher: inout Hasher) {
+ switch value {
+ case let value as Bool:
+ hasher.combine(value)
+ case let value as Int:
+ hasher.combine(value)
+ case let value as Int8:
+ hasher.combine(value)
+ case let value as Int16:
+ hasher.combine(value)
+ case let value as Int32:
+ hasher.combine(value)
+ case let value as Int64:
+ hasher.combine(value)
+ case let value as UInt:
+ hasher.combine(value)
+ case let value as UInt8:
+ hasher.combine(value)
+ case let value as UInt16:
+ hasher.combine(value)
+ case let value as UInt32:
+ hasher.combine(value)
+ case let value as UInt64:
+ hasher.combine(value)
+ case let value as Float:
+ hasher.combine(value)
+ case let value as Double:
+ hasher.combine(value)
+ case let value as String:
+ hasher.combine(value)
+ case let value as [String: AnyDecodable]:
+ hasher.combine(value)
+ case let value as [AnyDecodable]:
+ hasher.combine(value)
+ default:
+ break
+ }
+ }
+}
diff --git a/damus/Util/AnyCodable/AnyEncodable.swift b/damus/Util/AnyCodable/AnyEncodable.swift
@@ -0,0 +1,291 @@
+#if canImport(Foundation)
+import Foundation
+#endif
+
+/**
+ A type-erased `Encodable` value.
+
+ The `AnyEncodable` type forwards encoding responsibilities
+ to an underlying value, hiding its specific underlying type.
+
+ You can encode mixed-type values in dictionaries
+ and other collections that require `Encodable` conformance
+ by declaring their contained type to be `AnyEncodable`:
+
+ let dictionary: [String: AnyEncodable] = [
+ "boolean": true,
+ "integer": 42,
+ "double": 3.141592653589793,
+ "string": "string",
+ "array": [1, 2, 3],
+ "nested": [
+ "a": "alpha",
+ "b": "bravo",
+ "c": "charlie"
+ ],
+ "null": nil
+ ]
+
+ let encoder = JSONEncoder()
+ let json = try! encoder.encode(dictionary)
+ */
+@frozen public struct AnyEncodable: Encodable {
+ public let value: Any
+
+ public init<T>(_ value: T?) {
+ self.value = value ?? ()
+ }
+}
+
+@usableFromInline
+protocol _AnyEncodable {
+ var value: Any { get }
+ init<T>(_ value: T?)
+}
+
+extension AnyEncodable: _AnyEncodable {}
+
+// MARK: - Encodable
+
+extension _AnyEncodable {
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+
+ switch value {
+ #if canImport(Foundation)
+ case is NSNull:
+ try container.encodeNil()
+ #endif
+ case is Void:
+ try container.encodeNil()
+ case let bool as Bool:
+ try container.encode(bool)
+ case let int as Int:
+ try container.encode(int)
+ case let int8 as Int8:
+ try container.encode(int8)
+ case let int16 as Int16:
+ try container.encode(int16)
+ case let int32 as Int32:
+ try container.encode(int32)
+ case let int64 as Int64:
+ try container.encode(int64)
+ case let uint as UInt:
+ try container.encode(uint)
+ case let uint8 as UInt8:
+ try container.encode(uint8)
+ case let uint16 as UInt16:
+ try container.encode(uint16)
+ case let uint32 as UInt32:
+ try container.encode(uint32)
+ case let uint64 as UInt64:
+ try container.encode(uint64)
+ case let float as Float:
+ try container.encode(float)
+ case let double as Double:
+ try container.encode(double)
+ case let string as String:
+ try container.encode(string)
+ #if canImport(Foundation)
+ case let number as NSNumber:
+ try encode(nsnumber: number, into: &container)
+ case let date as Date:
+ try container.encode(date)
+ case let url as URL:
+ try container.encode(url)
+ #endif
+ case let array as [Any?]:
+ try container.encode(array.map { AnyEncodable($0) })
+ case let dictionary as [String: Any?]:
+ try container.encode(dictionary.mapValues { AnyEncodable($0) })
+ case let encodable as Encodable:
+ try encodable.encode(to: encoder)
+ default:
+ let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded")
+ throw EncodingError.invalidValue(value, context)
+ }
+ }
+
+ #if canImport(Foundation)
+ private func encode(nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws {
+ switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) {
+ case "B":
+ try container.encode(nsnumber.boolValue)
+ case "c":
+ try container.encode(nsnumber.int8Value)
+ case "s":
+ try container.encode(nsnumber.int16Value)
+ case "i", "l":
+ try container.encode(nsnumber.int32Value)
+ case "q":
+ try container.encode(nsnumber.int64Value)
+ case "C":
+ try container.encode(nsnumber.uint8Value)
+ case "S":
+ try container.encode(nsnumber.uint16Value)
+ case "I", "L":
+ try container.encode(nsnumber.uint32Value)
+ case "Q":
+ try container.encode(nsnumber.uint64Value)
+ case "f":
+ try container.encode(nsnumber.floatValue)
+ case "d":
+ try container.encode(nsnumber.doubleValue)
+ default:
+ let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "NSNumber cannot be encoded because its type is not handled")
+ throw EncodingError.invalidValue(nsnumber, context)
+ }
+ }
+ #endif
+}
+
+extension AnyEncodable: Equatable {
+ public static func == (lhs: AnyEncodable, rhs: AnyEncodable) -> Bool {
+ switch (lhs.value, rhs.value) {
+ case is (Void, Void):
+ return true
+ case let (lhs as Bool, rhs as Bool):
+ return lhs == rhs
+ case let (lhs as Int, rhs as Int):
+ return lhs == rhs
+ case let (lhs as Int8, rhs as Int8):
+ return lhs == rhs
+ case let (lhs as Int16, rhs as Int16):
+ return lhs == rhs
+ case let (lhs as Int32, rhs as Int32):
+ return lhs == rhs
+ case let (lhs as Int64, rhs as Int64):
+ return lhs == rhs
+ case let (lhs as UInt, rhs as UInt):
+ return lhs == rhs
+ case let (lhs as UInt8, rhs as UInt8):
+ return lhs == rhs
+ case let (lhs as UInt16, rhs as UInt16):
+ return lhs == rhs
+ case let (lhs as UInt32, rhs as UInt32):
+ return lhs == rhs
+ case let (lhs as UInt64, rhs as UInt64):
+ return lhs == rhs
+ case let (lhs as Float, rhs as Float):
+ return lhs == rhs
+ case let (lhs as Double, rhs as Double):
+ return lhs == rhs
+ case let (lhs as String, rhs as String):
+ return lhs == rhs
+ case let (lhs as [String: AnyEncodable], rhs as [String: AnyEncodable]):
+ return lhs == rhs
+ case let (lhs as [AnyEncodable], rhs as [AnyEncodable]):
+ return lhs == rhs
+ default:
+ return false
+ }
+ }
+}
+
+extension AnyEncodable: CustomStringConvertible {
+ public var description: String {
+ switch value {
+ case is Void:
+ return String(describing: nil as Any?)
+ case let value as CustomStringConvertible:
+ return value.description
+ default:
+ return String(describing: value)
+ }
+ }
+}
+
+extension AnyEncodable: CustomDebugStringConvertible {
+ public var debugDescription: String {
+ switch value {
+ case let value as CustomDebugStringConvertible:
+ return "AnyEncodable(\(value.debugDescription))"
+ default:
+ return "AnyEncodable(\(description))"
+ }
+ }
+}
+
+extension AnyEncodable: ExpressibleByNilLiteral {}
+extension AnyEncodable: ExpressibleByBooleanLiteral {}
+extension AnyEncodable: ExpressibleByIntegerLiteral {}
+extension AnyEncodable: ExpressibleByFloatLiteral {}
+extension AnyEncodable: ExpressibleByStringLiteral {}
+extension AnyEncodable: ExpressibleByStringInterpolation {}
+extension AnyEncodable: ExpressibleByArrayLiteral {}
+extension AnyEncodable: ExpressibleByDictionaryLiteral {}
+
+extension _AnyEncodable {
+ public init(nilLiteral _: ()) {
+ self.init(nil as Any?)
+ }
+
+ public init(booleanLiteral value: Bool) {
+ self.init(value)
+ }
+
+ public init(integerLiteral value: Int) {
+ self.init(value)
+ }
+
+ public init(floatLiteral value: Double) {
+ self.init(value)
+ }
+
+ public init(extendedGraphemeClusterLiteral value: String) {
+ self.init(value)
+ }
+
+ public init(stringLiteral value: String) {
+ self.init(value)
+ }
+
+ public init(arrayLiteral elements: Any...) {
+ self.init(elements)
+ }
+
+ public init(dictionaryLiteral elements: (AnyHashable, Any)...) {
+ self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first }))
+ }
+}
+
+extension AnyEncodable: Hashable {
+ public func hash(into hasher: inout Hasher) {
+ switch value {
+ case let value as Bool:
+ hasher.combine(value)
+ case let value as Int:
+ hasher.combine(value)
+ case let value as Int8:
+ hasher.combine(value)
+ case let value as Int16:
+ hasher.combine(value)
+ case let value as Int32:
+ hasher.combine(value)
+ case let value as Int64:
+ hasher.combine(value)
+ case let value as UInt:
+ hasher.combine(value)
+ case let value as UInt8:
+ hasher.combine(value)
+ case let value as UInt16:
+ hasher.combine(value)
+ case let value as UInt32:
+ hasher.combine(value)
+ case let value as UInt64:
+ hasher.combine(value)
+ case let value as Float:
+ hasher.combine(value)
+ case let value as Double:
+ hasher.combine(value)
+ case let value as String:
+ hasher.combine(value)
+ case let value as [String: AnyEncodable]:
+ hasher.combine(value)
+ case let value as [AnyEncodable]:
+ hasher.combine(value)
+ default:
+ break
+ }
+ }
+}