Mirror a property from a CodingKey

I have a Decodable class and I’m trying to validate it’s mapping to our API by:

  1. checking that a CodingKey exist for a property, and
  2. check the type of the matching property.

For the first task I can easily get a CodingKey by a string value, but for the 2nd task (validate the type) I need to use reflection, but I can’t find a way to get a key path or the property name from the CodingKey.

For example for:

class MyClass: Object, Decodable {
    enum CodingKeys: String, CodingKey {
        case name
        case itemCount = "item_count"
    var name: String = ""
    var itemCount: Int = 0

I need to verify that the property matching item_count is an Int, so I start by trying to init a CodingKey from the stringValue:

guard let key = MyClass. CodingKeys(stringValue: "item_count") else { return }

Which returns the relevant CodingKey as expected.

To mirror the property itemCount I than need either a key path for that key or the case name (i.e "itemCount" not "item_count"), however using CodingKeys.itemCount, everything I tried such as String(reflecting:...) and String(describing: ...) returns:

"CodingKeys(stringValue: "item_count", intValue: nil)" (I guess it’s overridden by default.)

So I can’t get the case name (not string value), or a key path. Any ideas how to do this?

What kind of validation is that? (Are you validating the code against some formal spec like Swagger perhaps?)

We have a bunch of business types that are decoded from our REST API responses. In order to test that, we have a suite of sample JSON responses from the API and for each of our business types we verify that the type decodes successfully from all related JSON samples:

func testSpecConformance() {
    for sample in APISample.PaymentCard.all {
        XCTAssertNoThrow(try Card<PaymentCard>.decodeJSON(data: sample.data))

Plus we have a detailed decoding test that takes a JSON response sample and verifies that the various properties are decoded as expected. When the API spec changes, the JSON samples are changed, too, and if they no longer match the code our unit tests will fail.

This is quite different from what you’re trying to do, so I guess I’m just offering a different perspective. Validating the types against some API spec (if that’s what you’re trying to do?) seems like a noble goal, but at least in our case it would fail quite quickly, as we often design types in Swift quite differently from what the responses look like.

In Swift 4.0 String(describing: CodingKeys.foo) would return foo which was guaranteed to match the property. Unfortunately now in 4.1 CodingKey implements CustomStringConvertible, and I too haven’t found a way to dynamically obtain the name of the enum case.

I opted instead to require that all my Codable classes also provided a mapping between property names and coding keys, e.g:

  var codingKeysByPropertyName: [String: CodingKey] {
    return ["someVariable": CodingKeys.someVariable]

The extra boilerplate is annoying for sure. I also validate at runtime that all class/struct properties have an associated coding key in the dict, just to be safe.