In this new chapter we will cover more advanced features about Swift enumerations, remember to read the first chapter if you missed it or you want to refresh the basic concepts of Swift enums.
Associated Values
Enumeration cases have their own value and type, and sometimes it is useful to store additional data of other types alongside an enum case. Associated values are a fantastic way to achieve that by attaching custom information along with the case value, allowing this information to be different each time you use that enum case in your code.
Here’s an example of an enumeration that defines a set of actions that users can execute in order to modify a layer. A layer can be resized, rotated and filled with a color. The enum case resize
has two associated values that define the new width and height values of the layer, the enum case rotate
has one associated value which indicates the rotation degrees, and the enum case fill
has one associated value that indicates the color of the layer.
enum LayerActions { | |
case scale(width: Double, height: Double) | |
case rotate(degrees: Double) | |
case fill(hex: String) | |
} | |
let scale = LayerActions.scale(width: 120, height: 60) | |
let fill = LayerActions.fill(hex: "#FFFFFF") |
The associated values can be accessed using a switch statement, and they can be extracted as constants (with the let
prefix) or as variables (with the var
prefix).
let fill = LayerActions.fill(color: "#FFFFFF") | |
switch fill { | |
case .scale(let width, let height): | |
print("Layer size: \(width)x\(height)") | |
case .rotate(let degrees): | |
print("Layer rotation: \(degrees)") | |
case .fill(let color): | |
print("Layer color: \(color)") | |
} |
Methods
Enumerations in Swift are first-class types in their own right. They adopt many features traditionally supported only by classes, for example, instance methods to provide functionality related to the values the enumeration represents.
Methods on enumerations exist for every enum case. If you want to provide specific code for every enum case, you need a switch statement to determine each case.
enum Polygon { | |
case triangle, quadrilateral, pentagon, hexagon | |
func description() -> String { | |
switch self { | |
case .triangle: | |
return "A triangle is a polygon with three egdes" | |
case .quadrilateral: | |
return "A quadrilateral is a polygon with four egdes" | |
case .pentagon: | |
return "A pentagon is a polygon with five egdes" | |
case .hexagon: | |
return "An hexagon is a polygon with six egdes" | |
} | |
} | |
} |
Mutating Methods
By default, the properties of an enumeration cannot be modified from within its instance methods. However, if you need to modify your enumeration you can use mutating methods. Mutating methods for enumerations can set the implicit self parameter to be a different case from the same enumeration.
Here’s an example that defines an enumeration for a temperature alarm with two states, enabled
and disabled
. The enumeration changes its state when the temperature reaches an specific threshold.
enum Alarm { | |
case enabled, disabled | |
mutating func newReading(temperature: Double) { | |
if (temperature > 300) { | |
self = .enabled | |
} else { | |
self = .disabled | |
} | |
} | |
func description() -> String { | |
switch self { | |
case .enabled: | |
return "Alarm is enabled" | |
case .disabled: | |
return "Alarm is disabled" | |
} | |
} | |
} | |
var ovenAlarm = Alarm.disabled | |
ovenAlarm.newReading(temperature: 340) | |
ovenAlarm.description() // "Alarm is enabled" |
Properties
Event though enumerations can’t have stored properties, they can have computed properties to provide additional information about the enumeration’s current value.
The example below modifies the Polygon
enumeration in order to add a new property edges
. This new property returns the number of edges of the enum case. The method description()
has also been updated in order to use the new property.
enum Polygon { | |
case triangle, quadrilateral, pentagon, hexagon | |
var edges: Int { | |
switch self { | |
case .triangle: | |
return 3 | |
case .quadrilateral: | |
return 4 | |
case .pentagon: | |
return 5 | |
case .hexagon: | |
return 6 | |
} | |
} | |
func description() -> String { | |
switch self { | |
case .triangle: | |
return "A triangle is a polygon with \(self.edges) egdes" | |
case .quadrilateral: | |
return "A quadrilateral is a polygon with \(self.edges) egdes" | |
case .pentagon: | |
return "A pentagon is a polygon with \(self.edges) egdes" | |
case .hexagon: | |
return "An hexagon is a polygon with \(self.edges) egdes" | |
} | |
} | |
} |
If you find this post helpful, please recommend it for others to read. In the next chapter we will cover more advanced enumeration usages such as protocols, extensions, generics and custom initializers.
Top comments (0)