diff --git a/Sources/Apollo/JSONStandardTypeConversions.swift b/Sources/Apollo/JSONStandardTypeConversions.swift index cf350a1e19..aede02f7f1 100644 --- a/Sources/Apollo/JSONStandardTypeConversions.swift +++ b/Sources/Apollo/JSONStandardTypeConversions.swift @@ -105,6 +105,22 @@ extension Optional: JSONEncodable { return NSNull() case .some(let wrapped as JSONEncodable): return wrapped.jsonValue + + // WORKAROUND: For reasons I don't totally understand, when the underlying type is `Any`, + // even though all of these conform to `JSONEncodable`, the `as JSONEncodable` above + // fails, and we need to handle them individually. + case .some(let wrapped as String): + return wrapped.jsonValue + case .some(let wrapped as Int): + return wrapped.jsonValue + case .some(let wrapped as Double): + return wrapped.jsonValue + case .some(let wrapped as Bool): + return wrapped.jsonValue + case .some(let wrapped as [String: Any?]): + return wrapped.jsonValue + case .some(let wrapped as [Any?]): + return wrapped.jsonValue default: fatalError("Optional is only JSONEncodable if Wrapped is") } @@ -129,6 +145,16 @@ extension Dictionary: JSONEncodable { } } +extension Dictionary: JSONDecodable { + public init(jsonValue value: JSONValue) throws { + guard let dictionary = value as? Dictionary else { + throw JSONDecodingError.couldNotConvert(value: value, to: Dictionary.self) + } + + self = dictionary + } +} + extension Array: JSONEncodable { public var jsonValue: JSONValue { return map() { element -> (JSONValue) in diff --git a/Tests/ApolloTests/JSONTests.swift b/Tests/ApolloTests/JSONTests.swift index e8c5335579..83ea39f01b 100644 --- a/Tests/ApolloTests/JSONTests.swift +++ b/Tests/ApolloTests/JSONTests.swift @@ -38,4 +38,39 @@ class JSONTests: XCTestCase { XCTAssertFalse(value ~= JSONDecodingError.nullValue) XCTAssertFalse(value ~= JSONDecodingError.missingValue) } + + func testJSONDictionaryEncodingAndDecoding() throws { + let jsonString = """ +{ + "a_dict": { + "a_bool": true, + "another_dict" : { + "a_double": 23.1, + "an_int": 8, + "a_string": "LOL wat" + }, + "an_array": [ + "one", + "two", + "three" + ], + "a_null": null + } +} +""" + let data = try XCTUnwrap(jsonString.data(using: .utf8)) + let json = try JSONSerializationFormat.deserialize(data: data) + XCTAssertNotNil(json) + + let dict = try Dictionary(jsonValue: json) + XCTAssertNotNil(dict) + + let reserialized = try JSONSerializationFormat.serialize(value: dict) + XCTAssertNotNil(reserialized) + + let stringFromReserialized = try XCTUnwrap(String(bytes: reserialized, encoding: .utf8)) + XCTAssertEqual(stringFromReserialized, """ +{"a_dict":{"a_bool":1,"a_null":null,"an_array":["one","two","three"],"another_dict":{"a_double":23.100000000000001,"a_string":"LOL wat","an_int":8}}} +""") + } }