
Enumeration Syntax #
1. Core Concept #
- Explanation: In Swift, an
Enumerationdefines a common type for a group of related values. It has its own properties, methods, and can even implement Protocols. By default, Swift Cases are not automatically assigned integer values (like 0, 1, 2). - Key Syntax:
enum,case
2. Example #
enum CompassPoint {
case north
case south
case east
case west
}
var directionToHead = CompassPoint.west
directionToHead = .east // Type inferred, type name can be omittedLogic:
Here, an enumeration named CompassPoint is defined with four directions. Note that once the type of the variable directionToHead is inferred as CompassPoint, subsequent assignments can use the shorthand syntax .east. This makes the code very concise.
3. C# Comparison #
Concept Mapping: This corresponds to enum in C#.
C# Example:
enum CompassPoint {
North,
South,
East,
West
}
// Must write full name in C# (unless using using static)
var direction = CompassPoint.West;Key Differences:
- Syntax: C# defaults
Northto0. Swift’snorthis justnorth; it does not equal0. - Behavior: Swift’s dot syntax (
.east) relies on Type Inference, which is more concise than C#. In C#, an Enum is essentially a wrapper for a numeric Value Type; in Swift, an Enum is a more abstract Algebraic Data Type.
Matching Enumeration Values with a Switch Statement #
1. Core Concept #
- Explanation: Swift uses
switchto match enumeration values. The most important aspect is Exhaustiveness, meaning you must handle every possiblecase, otherwise the compiler will throw an error. This forces developers to check all related logic when adding new enumeration members. - Key Syntax:
switch,case,default
2. Example #
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}Logic:
This code checks the value of directionToHead. Since we have listed all four directions, the compiler confirms that all cases are covered, so a default branch is not needed.
3. C# Comparison #
Concept Mapping: Corresponds to the switch statement in C#.
C# Example:
switch (direction)
{
case CompassPoint.North:
Console.WriteLine("...");
break;
case CompassPoint.South:
Console.WriteLine("Watch out for penguins");
break;
// C# compiler usually doesn't force you to list all enum cases,
}Key Differences:
- Behavior: Swift’s forced Exhaustiveness is an excellent safety feature. In C#, if you forget to handle a new Enum member, the program might silently execute past it; in Swift, the code will not compile, forcing you to fix it.
Iterating over Enumeration Cases #
1. Core Concept #
- Explanation: Sometimes we need to retrieve a list of all possible cases in an enumeration (e.g., to create a menu). Swift provides a standard way to do this: make the Enum conform to the
CaseIterableprotocol. - Key Syntax:
CaseIterable,.allCases
2. Example #
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
for beverage in Beverage.allCases {
print(beverage)
}Logic:
After adding CaseIterable, the compiler automatically generates an allCases collection property. You can loop through it or count the items directly.
3. C# Comparison #
Concept Mapping: C# has no built-in interface for this, typically relying on Reflection.
C# Example:
enum Beverage { Coffee, Tea, Juice }
// Modern C# (.NET 5+) uses generic methods
foreach (Beverage b in Enum.GetValues<Beverage>())
{
Console.WriteLine(b);
}Key Differences:
- Syntax: Swift’s
CaseIterableis generated at compile-time, making it more type-safe and performant. C#’sEnum.GetValuesrelies on runtime reflection, which has higher overhead and more verbose syntax.
Associated Values #
1. Core Concept #
- Explanation: In Swift, each case of an Enum can carry custom data of different types. This is called “Associated Values.” This turns Enums into Discriminated Unions or Tagged Unions.
- Key Syntax:
case name(Type) - Note:
This behavior is similar to discriminated unions, tagged unions, or variants in other languages.
2. Example #
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}Logic:
A Barcode can be a upc composed of four integers, OR a qrCode composed of a string. It cannot be both simultaneously. In the switch statement, we can use let to destructure this internal data for use.
3. C# Comparison #
Concept Mapping: Standard C# enum cannot do this at all. To simulate this pattern in C#, you typically need to use Abstract Classes with inheritance, or third-party libraries like OneOf.
C# Example (Simulation):
// Modern C# can use records to simplify Discriminated Unions simulation
abstract record Barcode;
record Upc(int N1, int N2, int N3, int N4) : Barcode;
record QrCode(string Code) : Barcode;
// Requires type checking (Pattern Matching) when used
Barcode bar = new QrCode("ABC");
switch (bar)
{
case Upc u:
Console.WriteLine($"UPC: {u.N1}...");
break;
case QrCode q:
Console.WriteLine($"QR: {q.Code}");
break;
}Key Differences:
- Behavior: Swift Enums are Value Types and very lightweight. The C# example above uses Classes/Records (Reference Types), which have a heavier structure.
- Mindset Shift: C# developers are used to viewing Enums as “State Flags”; Swift developers view Enums as “State Containers carrying data.”
Raw Values #
1. Core Concept #
- Explanation: Swift supports mapping each case to a fixed value via
Raw Values. These values are fixed at definition time and must be of the same type (e.g., all Int or all String). - Key Syntax:
enum Name: Type,.rawValue
2. Example #
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
enum CompassPoint: String {
case north, south, east, west
}
// CompassPoint.south.rawValue is "south" (implicit assignment)Logic:
By adding : Type after the Enum name, we define the type of the raw values. Swift even supports String as a raw value; if not specified, it will conveniently use the case name as the string value.
3. C# Comparison #
Concept Mapping: This is the standard behavior of C# enum, but Swift supports more types.
C# Example:
enum Planet : int { // C# can only be integer types (byte, int, long...)
Mercury = 1,
Venus,
Earth
}Key Differences:
- Syntax: C# Enums are restricted to integers under the hood. Swift Raw Values can be
String,Character,Int,Float, etc. - Behavior: In Swift, accessing the underlying value requires explicitly calling the
.rawValueproperty, you cannot just cast it like in C#.
Initializing from a Raw Value #
1. Core Concept #
- Explanation: When an Enum has raw values, you can use that raw value to reverse-initialize an Enum instance. However, since the passed value might be invalid (e.g., passing 999 when no corresponding case exists), this initializer is Failable, returning an Optional (
Enum?). - Key Syntax:
init(rawValue:) - Note:
The raw value initializer is a failable initializer, because not every raw value will return an enumeration case.
2. Example #
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet type is Planet?
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}Logic:
Using Planet(rawValue: 11) attempts to create an instance. Since 11 is out of the defined range, it returns nil. Here, Swift’s if let (Optional Binding) is used to safely handle the result.
3. C# Comparison #
Concept Mapping: C# casting (Enum)intValue.
C# Example:
int position = 11;
Planet p = (Planet)position; // C# allows this! The value of p is now 11, even if not defined
if (Enum.IsDefined(typeof(Planet), p)) {
// Safety check
} else {
Console.WriteLine("Invalid planet");
}Key Differences:
- Behavior: C# allows you to cast any integer to an Enum, even if that value is not defined in the Enum, which can lead to runtime logic errors. Swift’s
init(rawValue:)forces the return of anOptional, compelling the developer to handle the “conversion failure” scenario, resulting in much higher safety.
Recursive Enumerations #
1. Core Concept #
- Explanation: When an Enum’s Associated Value contains the Enum itself, it is a Recursive Enumeration. This is very useful for describing tree structures (like mathematical expressions). Since Enums store their associated values inline by default, a recursive definition implies the type size cannot be determined at compile time, so
indirectis needed to tell the compiler to use indirect storage (boxing). - Key Syntax:
indirect enum
2. Example #
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product)) // Prints "18"Logic:
Here a mathematical expression structure is defined. (5 + 4) * 2 is elegantly represented using the Enum structure. The evaluate function calculates the result by recursively calling itself.
3. C# Comparison #
Concept Mapping: C# Enums do not support this feature. In C#, this is typically implemented using a recursive Class structure (i.e., the Composite Pattern).
C# Example:
abstract class Expression { }
class Number : Expression { public int Value; }
class Addition : Expression { public Expression Left; public Expression Right; }
// ...Key Differences:
- Syntax: Swift allows using
indirectwithin a Value Type (Enum) to implement recursive structures, which is syntactically very concise and follows a functional programming style. C# must use Reference Types (Classes) to build this kind of data structure.