Hey folks,
I'm working on an issue surrounding evolvability of JSON entities. In order to support this, I need to be able to store and later serialize keys that aren't known by the Swift entity.
For example:
struct V1: Codable {
var name: String
}
struct V2: Codable {
var name: String
var age: Int
}
If a remote endpoint sends V2 to a client that only understands V1, then the unknown properties will be saved and encoded when the entity is sent back over the wire.
As we can't know the types of the unknown values, we can't use the standard process for encoding/decoding (array and dict values aren't necessarily homogenous so we just can't blindly cast)
So I've started thinking about a reflective solution.
I've introduced a protocol that consumers of our library may implement if they care about evolvability:
protocol Evolvable {
var evolvableUnknowns: [String : Any] { get set }
}
The entities previously referenced would implement this protocol. I then surface two API calls, one for encoding and the other for decoding that they will be responsible for invoking. These calls will do the heavy lifting.
Decoding was simple. Reflect the decoder to get the storage and store all keys that aren't handled by this entity:
let container = try decoder.container(keyedBy: UnknownCodingKey.self)
var unknownKeyValues = [String: Any]()
let decoderMirror = Mirror(reflecting: decoder)
let containers = decoderMirror.descendant("storage", "containers")!
let array = containers as! Array<Any>
let d = array[0]
let dict = d as! Dictionary<String,Any>
for key in container.allKeys {
guard !knownKeys.contains(key.stringValue) else { continue }
unknownKeyValues[key.stringValue] = dict[key.stringValue]!
}
return unknownKeyValues
However, encoding is proving difficult. Since reflection in swift is read-only, I think my only choice is to try using UnsafeMutablePointers.
So the Encoder instance has a private variable called storage which maintains a stack (array) of containers.
I need to get a writeable reference to this stack and replace the value at index 0.
However, the documentation for UnsafeMutablePointers is fairly thin. I'm not quite sure how I can walk the object to get the array and set a new value.
Thoughts?