Enumeration - Swift Programming

Enum

    1. Introduction
    2. Raw Values
    3. Associated Values
    4. CaseIterable
    5. Hashable
    6. Recursive Enumeration

Introduction:

An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.
 
Enumerations in Swift are much more flexible, and don’t have to provide a value for each case of the enumeration.
 
Swift enumeration cases don’t have an integer value set by default, unlike languages like C and Objective-C. Enum cases don’t implicitly equal 0, 1, 2 and 3. Instead, the different enumeration cases are values in their own right, with an explicitly defined type of Enum Defined.

Enumerations in Swift are first-class types.
They adopt many features traditionally supported only by classes, such as
1. computed properties to provide additional information about the enumeration’s current value, and
2. instance methods to provide functionality related to the values the enumeration represents.

Enumerations can also define initialisers to provide an initial case value and can be extended to expand their functionality beyond their original implementation; and can conform to protocols to provide standard functionality.

Syntax:

enum EnumName: ProtocolConfirmation {

    case enumCaseA

    case enumCaseB

}


Example

enum SampleEnumOne {

    case sampleCaseA

    case sampleCaseB

}


Note:

Enum 'case' is not allowed outside of an enum. Enum extension wouldn't allow to add new cases.

 
extension SampleEnumOne {
     
case sampleCaseC
 }
 
 1. They are meant for confirming other protocols and implementing the protocols
 2. Defining Computed properties to provide additional information about the enumeration’s current value, and
 3. Defining Instance methods to provide functionality related to the values the enumeration represents.

extension SampleEnumOne {

    init?(rawValue: String) {

        switch value {

        case "sampleCaseA":

            self = .sampleCaseA

        case "sampleCaseB":

            self = .sampleCaseB

        default:

            return nil

        }

    }

}


Raw Values:

As an alternative to associated values, enumeration cases can come prepopulated with default values (called raw values), which are all of the same type.

Raw values can be strings, characters, or any of the integer or floating-point number types. Each raw value must be unique within its enumeration declaration.

Raw values are not the same as associated values. Raw values are set to pre-populated values when you first define the enumeration in your code, like the three ASCII codes above. The raw value for a particular enumeration case is always the same. Associated values are set when you create a new constant or variable based on one of the enumeration’s cases, and can be different each time you do so.

let sampleInsOne = SampleEnumOne(rawValue: "sampleCaseA")

print(sampleInsOne as Any) // prints ".sampelCaseA"


let sampleInsTwo = SampleEnumOne(rawValue: "sampleCaseC")

print(sampleInsTwo as Any) // prints "nil"



Associated Values:

Enumeration cases can specify associated values of any type to be stored along with each different case value. And it varies each time you use that case as a value in your code.

enum SampleEnumTwo {

    case sampleAssociatedCaseA(String)

    case sampleAssociatedCaseB(sampleValue: String)

}


example:

enum Barcode {

    case upc(Int, Int, Int, Int)

    case qrCode(String)

}

 
 Or

enum Barcode {

    case upc(systemCode: Int, manufacturerCode: Int, productCode: Int, checkBitCode: Int)

    case qrCode(productCode: String)

}


Usage:
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).")
}


CaseIterable:

Enums - Simple

extension SampleEnumOneCaseIterable {

    

}

print(SampleEnumOne.allCases) // prints all cases


extension SampleEnumOne {

    static func checkExistsOrNot(value: String) -> SampleEnumOne? {

        return self.allCases.first"\($0)" == value }

    }

}


let sampleInsOne = SampleEnumOne.checkExistsOrNot(value: "sampleCaseA")

print(sampleInsOne as Any) // prints ".sampelCaseA"


let sampleInsTwo = SampleEnumOne.checkExistsOrNot(value: "sampleCaseC")

print(sampleInsTwo as Any) // prints "nil"


Enums - Associated Values

Enum having associated values does not conform to protocol 'CaseIterable' implicitly. You need to implement allcases for associated values of enum as below, where as it not required with normal cases


extension SampleEnumTwoCaseIterable { 

    typealias AllCases = [SampleEnumTwo]

    

    static var allCases: [SampleEnumTwo] {

        return [.sampleAssociatedCaseA("SampleAssociatedCaseA"), .sampleAssociatedCaseB(sampleValue: "SampleAssociatedCaseB")]

    }

}

print(SampleEnumTwo.allCases) // prints all cases



Enums - Combination - Simple & Associated Values


enum SampleEnumThree {

    case sampleCaseA

    

    case sampleAssociatedCaseA(value: String)

}


extension SampleEnumThreeCaseIterable {

    typealias AllCases = [SampleEnumThree]

    

    static var allCases: [SampleEnumThree] {

        return [.sampleCaseA, .sampleAssociatedCaseA(value: "SampleAssociatedCaseA")]

    }

}

print(SampleEnumThree.allCases) // prints all cases



Hashable:

Confirming with Hashable. A type that can be hashed into a Hasher to produce an integer hash value.


enum SampleEnumFour {

    case Word(String)

    case Number(Int)

}


extension SampleEnumFourHashable {

    func hash(into hasher: inout Hasher) {

        switch self {

        case .Word(let value):

            hasher.combine(value)

        case .Number(let value):

            hasher.combine(value)

        }

    }


    static func ==(lhs: SampleEnumFour, rhs: SampleEnumFour) -> Bool {

        switch (lhs, rhs) {

        case (.Word(let lhsValue), .Word(let rhsValue)):

            return lhsValue == rhsValue

        case (.Number(let lhsValue), .Number(let rhsValue)):

            return lhsValue == rhsValue

        default:

            return false

        }

    }

}


let sampleWord = SampleEnumFour.Word("SampleWord")

let sampleNumber = SampleEnumFour.Number(1)


print(sampleWord == sampleNumber// prints false

print(sampleNumber == sampleNumber// prints true



Recursive Enumerations:

A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases.

enum ArithmeticExpression {

    case number(value: Int)

    indirect case addition(expressionOne: ArithmeticExpression, expressionTwo: ArithmeticExpression)

    indirect case multiplication(expressionOne: ArithmeticExpression, expressionTwo: ArithmeticExpression)

}


You indicate that an enumeration case is recursive by writing indirect before it, which tells the compiler to insert the necessary layer of indirection.

indirect enum ArithmeticExpression {

    case number(value: Int)

    case addition(expressionOne: ArithmeticExpression, expressionTwo: ArithmeticExpression)

    case multiplication(expressionOne: ArithmeticExpression, expressionTwo: ArithmeticExpression)

}


You can also write indirect before the beginning of the enumeration to enable indirection for all of the enumeration’s cases that have an associated value

indirect enum ArithmeticExpression {

    case number(Int)

    case addition(ArithmeticExpressionArithmeticExpression)

    case multiplication(ArithmeticExpressionArithmeticExpression)

}


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)

    }

}


let multiplyArithmeticExpression = .multiplication(.number(10), .number(10))

let addArithmeticExpression = .addition(multiply, .number(8))

print(evaluate(add)) // prints 108



Reference Links:

  1. https://gist.github.com/ixcoder001/fb7946bccd9ca3c594f4871a410b770c


Comments