Enum variables in Swift?
Solution 1
For Swift enum
, you can only use (String|Integer|Float)LiteralConvertible
types as the raw value. If you want to use existing type(e.g. CGPoint
) for the raw value, you should follow @Alex answer.
I will provide 2 alternatives in this answer
Very simple solution
enum Test: String {
case A = "foo:1"
case B = "bar:2"
var var1: String {
return split(self.rawValue, { $0 == ":" })[0]
}
var var2: Int {
return split(self.rawValue, { $0 == ":" })[1].toInt()!
}
}
let test = Test.A
println(test.var1) // -> "foo"
You don't like this? go to next one :)
Behavior emulation using struct
and static
constants
struct Test {
let var1: String
let var2: Int
private init(_ var1:String, _ var2:Int) {
self.var1 = var1
self.var2 = var2
}
}
extension Test {
static let A = Test("foo", 1)
static let B = Test("bar", 2)
static let allValues = [A, B]
}
let test = Test.A
println(test.var1) // -> "foo"
But of course, struct
lacks some features from enum
. You have to manually implement it.
Swift enum
implicitly conforms Hashable
protocol.
extension Test: Hashable {
var hashValue:Int {
return find(Test.allValues, self)!
}
}
func ==(lhs:Test, rhs:Test) -> Bool {
return lhs.var1 == rhs.var1 && lhs.var2 == rhs.var2
}
Test.A.hashValue // -> 0
Test.B.hashValue // -> 1
Test.A == Test.B // -> false
In the first code, we already have allValues
that is corresponding to values()
in Java. valueOf(...)
in Java is equivalent to init?(rawValue:)
in RawRepresentable
protocol in Swift:
extension Test: RawRepresentable {
typealias RawValue = (String, Int)
init?(rawValue: RawValue) {
self.init(rawValue)
if find(Test.allValues, self) == nil{
return nil
}
}
var rawValue: RawValue {
return (var1, var2)
}
}
Test(rawValue: ("bar", 2)) == Test.B
Test(rawValue: ("bar", 4)) == nil
And so on...
I know this is not "in a generic way". And one thing we never can emulate is "Matching Enumeration Values with a Switch Statement" feature in Swift. you always need default
case:
var test = Test.A
switch test {
case Test.A: println("is A")
case Test.B: println("is B")
default: fatalError("cannot be here!")
}
Solution 2
Yes it is a design decision but you can kind of work around it in some cases. The idea is to extend a Type to conform to one of: integer-literal floating-point-literal string-literal
The solution can be found here Bryan Chen's solution: How to create enum with raw type of CGPoint?
The second solution presented there by Sulthan may also be a way to go for you.
User
Updated on July 28, 2022Comments
-
User almost 2 years
I would like to associate multiple values with an enum value, in a generic way.
This can be done in Java:
enum Test { A("test", 2); final String var1; final int var2; Test (String var1, int var2) { this.var1 = var1; this.var2 = var2; } } public static void main(String []args){ Test test = Test.A; System.out.println(test.var1); }
But it looks like it's not possible with Swift? So far, according to docs, there are:
-
Associated values. Example (from docs):
enum Barcode { case UPCA(Int, Int, Int, Int) case QRCode(String) }
But this is not what I need.
-
Raw value. Example (from docs):
enum ASCIIControlCharacter: Character { case Tab = "\t" case LineFeed = "\n" case CarriageReturn = "\r" }
This would be what I need, but it can have only one value!
Is there an elegant solution for this...? Seems like a language design decision, as it would conflict with the associated values concept, at least in the current form. I know I could use e.g. a dictionary to map the enum values to the rest, but really missing to do this in one safe step, like in Java.
-