1

I'm working on a project to create a custom Decoder to deal with non JSON data.

Data to decode can be URLs or Strings. For example:

struct MyData: Decodable {
    let server: URL
    let version: String
}

In order to deal with such a type of decoding, I've implemented a class, called KeyedContainer, that implements the KeyedDecodingContainerProtocol.

For a String is quite simple since I can use the following method:

func decode(_ type: String.Type, forKey key: Key) throws -> String { }

For an URL, instead, I need to rely on the following:

func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Decodable { }

Within it, I do something like the following:

func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Decodable {
    try checkCanDecodeValue(forKey: key)

    guard let value = configuration[key.stringValue] else {
        let context = DecodingError.Context(codingPath: codingPath, debugDescription: "TODO")
        throw DecodingError.typeMismatch(type, context)
    }

    guard let url = URL(string: value) else {
        let context = DecodingError.Context(codingPath: codingPath, debugDescription: "TODO")
        throw DecodingError.valueNotFound(type, context)
    }

    return url as! T
}

where configuration is a struct that is passed along to KeyedContainer directly from the custom decoder.

Is this the right approach? What I'm not sure about is the as! T cast in order to make the compiler happy.

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190

1 Answers1

1

I don't believe you have to hard cast to T if you do:

    guard let url = URL(string: value) as? T else {
        let context = DecodingError.Context(codingPath: codingPath, debugDescription: "TODO")
        throw DecodingError.valueNotFound(type, context)
    }
Mathias Egekvist
  • 358
  • 3
  • 14