Sorry to revive an old thread!
I'm running into a very similar issue and I'm not sure if there's a cleaner way to implement encoding/decoding. I have a REST api that returns options as json array, ex: "["OptionB", "OptionC"]"
and I would like to form an OptionSet from it as well as re-encode to that format.
I have the following implementation that works, but requires a lot of manual updating if the supported options changes. Is there a better way to write this, or perhaps use Mirror
to get the behavior I'd like?
(note: I'm very unfamiliar with swift's reflection apis. Also, please let me know if this is off-topic; I can make a new thread if that would be better.)
struct ExampleOptions: OptionSet, Codable {
let rawValue: Int
static let optionA = ExampleOptions(rawValue: 1 << 0)
static let optionB = ExampleOptions(rawValue: 1 << 1)
static let optionC = ExampleOptions(rawValue: 1 << 2)
init(rawValue: Int) {
self.rawValue = rawValue
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var options = [ExampleOptions]()
while !container.isAtEnd {
let string = try container.decode(String.self)
let option = try ExampleOptions(string: string)
options.append(option)
}
let null = ExampleOptions(rawValue: 0)
self = options.reduce(null) { return $0.union($1) }
}
init(string: String) throws {
switch string {
case "OptionA": self = .optionA
case "OptionB": self = .optionB
case "OptionC": self = .optionC
default: throw NSError(domain: "ExampleOptions", code: 0, userInfo: nil) // make this more descriptive
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
if contains(.optionA) { try container.encode("OptionA") }
if contains(.optionB) { try container.encode("OptionB") }
if contains(.optionC) { try container.encode("OptionC") }
}
}