Several times I have had situations where the cases of an enum, conceptually, have both and integer value and a string value that they correspond to. This can be accomplished by having one be the raw value and the other be a computed property, but the choice of which should be the raw value is arbitrary, and the computed property is boilerplate.
A basic example:
enum Numbering: Int
{
case first = 1
case second = 2
var name: String
{
switch self {
case .first: return "first"
case .second: return "second"
}
}
}
The obvious thing to try here would be a tuple:
enum Numbering: (Int, String)
{
case first = (1, "first")
case second = (2, "second")
var number: Int { return self.rawValue.0 }
var name: String { return self.rawValue.1 }
}
...but that doesn't work. The basic problem seems to be that 1) enum raw values must be literals, and a tuple of literals doesn't count, and 2) the raw value type must be Equatable, and a tuple of equatables doesn't count.
Are there other good solutions for this? How much interest is there in addressing those tuple limitations?
It just doesn't work for the automatic synthesis of RawRepresentable. You can manually implement RawRepresentable for this pretty easily.
enum Tuples {
case one
case two
}
extension Tuples: RawRepresentable {
init?(rawValue: (Int, String)) {
switch rawValue {
case (1, "one"): self = .one
case (2, "two"): self = .two
default: return nil
}
}
var rawValue: (Int, String) {
switch self {
case .one: return (1, "one")
case .two: return (2, "two")
}
}
}
There have also been a variety of discussions around tuples and their conformance to various protocols. But really what you'd need here is to make the automatic synthesis for RawRepresentable more flexible, like removing the literal requirement.
Yeah, with a bit more fiddling I came up with basically the same thing, although I don't think you'd ever be initializing it with the whole tuple. More likely, you'd have one or the other individual type. So you'd have the initializer required by RawRepresentable, plus another initializer for each type in the tuple. The end result is that the individual raw types are equal citizens, but there's still a lot of boilerplate
enum Number: Int {
case first = 1
case second = 2
var name: String {
return String(describing: self)
}
}
let number: Number = .first
print(number.rawValue) // 1
print(number.name) // first
enum Numbering: NumberingValue{
case first = "1,first"
case second = "2,second"
var value: Int { return rawValue.value }
var name: String { return rawValue.name }
}
print(Numbering.first.value) //prints 1
print(Numbering.first.name) // prints "first"
If desired you could make NumberingValue use a Tuple internally, and I would put plenty of documentation/errors around it for production code.
I'm hoping some day we can define enum backing-values without requiring Literals. I think that would allow using anyRawRepresentable to back an enum.
That is a great solution for this situation, but we need to notice that it may cause inconsistent behavior when you make it objective-C accessible
@objc enum Number: Int {
case first = 1
case second = 2
var name: String {
return String(describing: self)
}
}
let number: Number = .first
print(number.rawValue) // 1
print(number.name) // Number(rawValue: 1)