Introduction
: Enumeration은 관련있는 value들의 group으로 하나의 타입을 정의하는 것을 말한다. Enumeration을 통해 type-safe way로 코드작성이 가능하다.

Swift의 Enumeration은 C의 Enumeration 보다는 조금 더 flexible하다. 모든 case에 value를 할당할 필요가 없다. value의 타입은 string, character, 모든 integer 또는 floating-point 종류의 타입이 가능하다.


Enumeration Syntax

enum SomeEnumeration {
    // enumeration definition goes here
}

예제 코드이다.

enum CompassPoint {
    case north
    case south
    case east
    case west
}

다른 언어의 Enumeration과 달리 각각의 case는 default integer 값을 가지지 않는다.

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

multiple value들은 single line으로 표현이 가능하다.

var directionToHead = CompassPoint.west

directionToHead = .east

directionToHead는 CompassPoint 타입으로 추론되었기 때문에 추후 .east 같이 앞에 타입을 생략해서 값 할당이 가능하다.


Matching Enumeration Values with a Switch Statement
: Enum을 Switch 문에서 쓰는 법을 알아보도록 하겠다.

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 sunrises")
    case .west:
        print("Where the skies are blue")
}
// Prints "Watch out for penguins"


Associated Values
: Swift의 enumeration은 각각의 case에 관련된 value들을 가질 수 있게 해준다. 예제를 보자.

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

// Prints "QR code: ABCDEFGHIJKLMNOP."

associated value 들이 모두 var거나 let이면 앞에 한번만 명시하면 된다.

switch productBarcode {
    case let .upc(numberSystem, manufacturer, product, check):
        print("UPC:\(numberSystem), \(manufacturer), \(product), \(check).")
    case let .qrCode(productCode):
        print("QR code: \(productCode).")
}

// Prints "QR code: ABCDEFGHIJKLMNOP."


Raw Values
: enumeration의 case는 각각 다른 타입의 associate value 들을 가질 수 있었다. 뿐만 아니라, 같은 타입의 Raw value들 또한 정의할 수 있다. 예제를 보자.

enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

Raw Value는 string, characters, integer or floating-point 종류의 타입으로 정의할 수 있다. 그리고 Raw Value는 enumeration 안에서 unique한 값이어야 한다. 

implicitly Assigned Raw Values
: raw value 타입이 Integer 또는 String 이라면, Swift는 자동으로 raw value를 대입해준다. integer일 경우 바로 앞의 case의 값에 1을 더한값이 자신의 raw value가 되고, first case의 raw value가 명시되어 있지 않으면 0이 된다. 

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

위의 경우 Planet.mercury는 1이고 Planet,venus는 2이다.

enum compassPoint: String {
    case north, south, east, west
}

위의 경우 raw value 타입이 String이고 아무값도 지정하지 않았기 때문에 raw value는 case 이름을 따라간다. CompassPoint.south의 raw value는 "south" 이다. 

let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"

Initializing from a Raw Value
: enumeration이 raw value 타입과 함께 정의되었다면 initializer의 param으로 rawValue값을 받아서 해당 enumeration case 또는 nil을 return 한다. 

let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus

위 예제에서 rawValue 파라미터에 대한 case가 없을때를 nil을 return 한다. 

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)")
}
// Prints "There isn't a planet at position 11"


Recursive Enumerations
: case의 associated value로 하나 또는 그 이상의 enumeration case를 포함하는 경우를 뜻한다. 이 경우 "indirect"를 명시해야 한다. 

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)    
}

모든 case에 대한 indirect을 허용하려면 enum 앞에 "indirect"을 명시하면 된다.

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


*Enumeration은 inherits를 할 수 없다.(structure도 마찬가지로 불가능.) 하지만 protocol을 confirm하는 형식으로 inherits 비슷하게 구현이 가능하다.
아래 예제를 보자.


protocol TableSection {

    static var rows: [Self] { get }

    

    var title: String { get }

    

    var mandatoryField: Bool { get }

}


extension TableSection {

    var mandatoryTitle: String {

        if mandatoryField {

            return "\(title)*"

        } else {

            return title

        }

    }

}


enum RegisterTableSection: Int, TableSection {

    case Username

    case Birthdate

    case Password

    case RepeatPassword

    

    static var rows: [RegisterTableSection] {

        return [.Username, .Password, .RepeatPassword]

    }

    

    var title: String {

        switch self {

        case .Username:

            return "Username"

        case .Birthdate:

            return "Date of birth"

        case .Password:

            return "Password"

        case .RepeatPassword:

            return "Repeat password"

        }

    }

    

    var mandatoryField: Bool {

        switch self {

        case .Username:

            return true

        case .Birthdate:

            return false

        case .Password:

            return true

        case .RepeatPassword:

            return true

        }

    }

}


class ViewController: UITableViewController {

    

    override func viewDidLoad() {

        super.viewDidLoad()

    }

    

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

        return 1

    }

    

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return RegisterTableSection.rows.count

    }

    

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        guard let row = RegisterTableSection(rawValue: indexPath.row) else {

            // This should never happen

            return UITableViewCell()

        }

        

        let cell = UITableViewCell()

        cell.textLabel?.text = row.mandatoryTitle

        return cell

        

    }

}


위 코드를 실행하면 tableView에 3개의 cell이 나타나게 되고, 각 cell에는 순서대로, "Username*", "Date of birth", "password*"이 나타나게 된다.


'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Properties  (0) 2017.04.01
[Swift 4.0] Classes and Structures  (0) 2017.04.01
[Swift 4.0] Closures  (0) 2017.04.01
[Swift 4.0] Functions  (0) 2017.04.01
[Swift 4.0] Control Flow  (0) 2017.04.01
Posted by 홍성곤
,