Hi all! I'm relatively new to Swift, coming from a primarily Objective-C background. I'm looking to write a parsing library in what I think might be a nicely typed way. I'm wondering if I'm thinking about this problem in the correct way.
Essentially, the task I'm trying to solve is to parse a stream of text containing keys associated with optional values. The parser declaration would allow for the adopter to provide a metatype conforming to LosslessStringConvertible that should be parsed from the text stream. The associated values may be required or optional. I'd like to be able to encode this in the metatype argument, but I'm not exactly sure how.
So for example, this may be used as follows:
parseValue(forKey: "key1", type: String?.self) // A string value may optionally be available in the input stream
parseValue(forKey: "key2", type: String.self) // A string value is required from the input stream
Thanks! That makes sense. But I'm still wondering if there might be cases where it'd be useful to encode an optionally optional metatype like this.
The way I really wanted to approach to this was to encode the parsable definition into a sequence of structs similar to the following:
struct ParseField<T: LosslessStringConvertible> {
let key: String
let valueType: T?.Type?
}
Since I'd be storing the metatype in an ivar, having two overloaded initializers wouldn't quite work here since they'd still need to be assigned to this shared ivar.
In retrospect, I'm thinking this isn't how metatypes should be used. Instead I've defined my own enum that encodes this more customized notion of optionality.
I'd love to have folks more familiar with Swift critique this approach. Is this better? Or am I replicating Swift optionals unnecessarily?
enum ExpectedValue {
case none
case optional(LosslessStringConvertible.Type)
case required(LosslessStringConvertible.Type)
}
struct ParseField {
let key: String
let value: ExpectedValue
}
let a = ParseField(key: "key1", value: .none)
let b = ParseField(key: "key2", value: .optional(String.self))
let c = ParseField(key: "key3", value: .required(Int.self))
Personally, I'd prefer to have isRequired as a separate variable
struct ParseField {
var key: String
var valueType: LosslessStringConvertible.Type
var isRequired: Bool
}
In any case, since you seem to be doing some deserialization, have you looked into Codable? Does it not work in your case?
You could also try to implement a new Encoder/Decoder.
It could be somewhat tedious, but it'd work well for general structure, and make it very simple for user.