mlinington
(Michelle Linington)
1
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
A couple options for a function declaration:
// Requires an optional metatype. Passing String.self fails
func parseValue<T: LosslessStringConvertible>(forKey: String, type: T?.Type?) {}
// Requires a non-optional metatype. Passing String?.self fails
func parseValue<T: LosslessStringConvertible>(forKey: String, type: T.Type?) {}
Is there a way I can declare a function that allows either an optional or a non-optional LosslessStringConvertible metatype?
Thanks!
Lantua
2
My first instinct is that it'd fit better to do a Codable style function signature.
// Non-optional T
func parseValue<T>(forKey: String, type: T.Type) where T: LosslessStringConvertible { ... }
// Optional T
func parseValueIfExist<T>(forKey: String, type: T.Type) where T: LosslessStringConvertible { ... }
Should you really need to put information in type, you could separate function into 2 overloads. Swift will choose the most specific one
func test<T>(_ value: T?.Type) where T: LosslessStringConvertible {
print("Optional", T.self)
}
func test<T>(_ value: T.Type) where T: LosslessStringConvertible {
print("Non-optional", T.self)
}
test(Int?.self) // "Optional Int"
test(String.self) // "Non-optional String"
mlinington
(Michelle Linington)
3
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.
mlinington
(Michelle Linington)
4
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))
Lantua
5
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.
mlinington
(Michelle Linington)
6
Thanks for the tip. I'll definitely be looking into that 
Lantua
7
Here a link for how the user should use it.
Here is some of my Encoder/Decoder implementation a while back. I didn't add much comment though
.
1 Like