I am trying to mock Apollo Queries using its init. It pretty much is taking in a dictionary to build the object up.
public init(unsafeResultMap: [String: Any]) {
self.resultMap = unsafeResultMap
}
So, I have decided to create Mock objects that have the same properties of the query objects while being Encodable (So we get the free JSON conversion, which can be represented as a string version dictionary).
For example:
class MockAnimalObject: Encodable {
let teeth: MockTeethObject
init(teeth: MockTeethObject) {
self.teeth = teeth
}
}
class MockTeethObject: Encodable {
let numberOfTeeth: Int
let dateOfTeethCreation: Date
init (numberOfTeeth: Int, dateOfTeethCreation: Date) {
self.numberOfTeeth = numberOfTeeth
self.dateOfTeethCreation = dateOfTeethCreation
}
}
The problem is, the Apollo conversion checks the types during the result map, which in our case is a string of [String: Encodable].
And this is where the Date encodable becomes a problem.
/// This property is auto-generated and not feasible to be edited
/// Teeth date for these teeth
public var teethCreationDate: Date {
get {
// This is the problem. resultMap["teethCreationDate"] is never going to be a Date object since it is encoded.
return resultMap["teethCreationDate"]! as! Date
}
set {
resultMap.updateValue(newValue, forKey: "teethCreationDate")
}
}
So, I am wondering if it is possible to override the encoder to manually set the date value as a custom type.
var container = encoder.singleValueContainer()
try container.encode(date) as Date // Something where I force it to be a non-encodable object
2
Answers
As pointed out in comments, JSON (Javascript object notation) is universal format and is not anyhow related to Date object in Swift after it is encoded. Therefore somewhere in the flow you need to make it
String
Double
or some other object type that can be encoded to JSON. Anyway, if you want to make encodingDate
to be easier you can take hold of some nativeJSONEncoder
functions, such as folowing:JSON has nothing to do with this. JSON is not any kind of dictionary. It’s a serialization format. But you don’t want a serialization format. You want to convert types to an Apollo ResultMap, which is
[String: Any?]
. What you want is a "ResultMapEncoder," not a JSONEncoder.That’s definitely possible. It’s just an obnoxious amount of code because Encoder is such a pain to conform to. My first pass is a bit over 600 lines. I could probably strip it down more and it’s barely tested, so I don’t know if this code works in all (or even most) cases, but it’s a start and shows how you would attack this problem.
The starting point is the source code for JSONEncoder. Like sculpture, you start with a giant block of material, and keep removing everything that doesn’t look like what you want. Again, this is very, very lightly tested. It basically does what you describe in your question, and not much else is tested.
The key changes, and where you’d want to explore further to make this work the way you want, are:
[String: Encodable]
) by just returning them. SeewrapEncodable
andwrapUntyped
If you want
[String: Any]
rather than[String: Any?]
(which is what ResultMap is), then you can tweak the types a bit. The only tricky piece is you would need to store something likenil as Any? as Any
in order to encode nil (or you could encode NSNull, or you could just not encode it at all if you wanted).Note that this actually returns
Any
, since it can’t know that the top level encodes an object. So you’ll need toas?
cast it to[String: Any?]
.To your question about using Mirror, the good thing about Mirror is that the code is short. The bad thing is that mirror is very slow. So it depends on how important that is. Not everything has the mirror you expect, however. For your purposes, Date has a "struct-like" Mirror, so you have to special-case it. But it’s not that hard to write the code. Something like this:
This time I didn’t bother with
Any?
, but you could probably expand it that way if you needed. You’d need to decide what you’d want to do about enums, tuples, and anything else you’d want to handle specially, but it’s pretty flexible. Just slow.