Enums - RaduG/swift_learning GitHub Wiki
Basic syntax
enum SomeEnum {
// enum code
}
Enumeration items are defined using case
.
enum CompassPoint {
case north
case east
case south
case west
}
// or
enum CompassPoint {
case north, east, south, west
}
Case values
The cases don't need to hold explicit values, as each case is a standalone type.
let direction = CompassPoint.east
// or, if the type can be inferred, the enum name can be dropped
let otherDirection: CompassPoint = .north
Matching cases
Use switch:
func whereAmIGoing(_ to: CompassPoint) {
switch to {
case .north:
print("You are going north")
case .east:
print("You are going east")
case .south:
print("You are going south")
case .west:
print("You are going west")
}
}
whereAmIGoing(.east) // or whereAmIGoing(CompassPoint.east)
Iterable enums
To make an Enum iterable, have the type implement the CaseIterable
protocol. The compiler automatically adds an implementation. The result is having the allCases
attribute, which can be iterated over:
enum CompassPoint: CaseIterable {
case north, east, south, west
}
for c in CompassPoint.allCases {
whereAmIGoing(c)
}
Associated values
It is also possible to store additional data with cases.
enum Colour {
case rgb(Int, Int, Int)
case cmyk(Double, Double, Double, Double)
case named(String)
}
let white = Colour.rgb(255, 255, 255)
let electricGreen = Colour.cmyk(1, 0, 1, 0)
let red = Colour.named("red")
This can be used in pattern matching:
func hasRed(_ colour: Colour) -> Bool {
switch colour {
case .rgb(let red, _, _) where red > 0:
return true
case .named("red"):
return true
default:
return false
}
}
hasRed(Colour.rgb(0, 100, 200)) // false
hasRed(Colour.rgb(10, 0, 0)) // true
hasRed(Colour.cmyk(0.2, 0.3, 0.75, 0)) // false
hasRed(Colour.named("red")) // true
If all the associated values should be captured as part of the pattern match, use a single var/let:
func printIsGray(_ colour: Colour) {
switch colour {
case let .rgb(red, green, blue) where (
red > 0
&& red < 255
&& red == green
&& green == blue
):
print("\(colour) is gray")
case let .cmyk(cyan, magenta, yellow, black) where (
cyan == 0
&& cyan == magenta
&& magenta == yellow
&& black > 0
&& black < 1
):
print("\(colour) is gray")
case .named("gray"), .named("grey"):
print("\(colour) is gray")
default:
print("\(colour) is not gray")
}
}
printIsGray(Colour.rgb(255, 255, 255))
printIsGray(Colour.rgb(245, 245, 245))
printIsGray(Colour.cmyk(0, 0, 0, 0))
printIsGray(Colour.cmyk(0, 0, 0, 1))
printIsGray(Colour.cmyk(0, 0, 0, 0.75))
printIsGray(Colour.named("gray"))
printIsGray(Colour.named("grey"))
printIsGray(Colour.named("blue"))
Raw values
Cases can also have values. Those are constants and are effectively static values associated to each case. For example:
enum WhitespaceSequence: String {
case tab = "\t"
case space = " "
case newLine = "\n"
case carriageReturn = "\r"
}
If the type is Int, values can be automatically assigned consecutively:
enum Rank: Int, CaseIterable {
case two = 2, three, four, five, six, seven, eight, nine, ten
case jack = 12, queen, king, ace
}
for rank in Rank.allCases {
print("\(rank): \(rank.rawValue)")
}
Note that the raw value can be accessed via the .rawValue
attribute of an enum case.
Enums with raw values also have a constructor taking a raw value as argument and creating an instance of its matching enum case.
// Returns an optional
print(Rank(rawValue: 7)!)
Recursive enumerations
Enum cases can refer to other enum cases, creating recursive definitions. Recursive cases must be marked with the indirect
keyword
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case subtraction(ArithmeticExpression, ArithmeticExpression)
}
// or
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case subtraction(ArithmeticExpression, ArithmeticExpression)
}
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case subtraction(ArithmeticExpression, ArithmeticExpression)
func describe() -> String {
switch self {
case let .number(value):
return "\(value)"
case let .addition(lhs, rhs):
return "\(lhs.describe()) + \(rhs.describe())"
case let .subtraction(lhs, rhs):
return "\(lhs.describe()) - \(rhs.describe())"
}
}
}
let exp: ArithmeticExpression = .addition(
.subtraction(
.number(10), .addition(
.number(12), .number(15)
)
),
.number(25)
)
print(exp.describe())