Enum

어떠한 객체나 UI같은 하나의 상태를 타입으로 나타낼 때 열거형(enum)을 자주 사용했었습니다.

enum State {
    case run
    case walk
    case sleep
    case eat
    case seat
}
// 배열로 사용한다면..?
var state = ["run", "walk", "sleep", "eat", "seat"]

배열로 사용하면 인덱스 범위를 넘어가거나 올바른 문자열을 받지 못하는 사이드 이펙트를 경험하기도 했습니다.

이를 열거형으로 사용하면 정해진 범위의 타입으로 지정할 수 있어서 코드 가독성과 사이드이펙트를 줄일 수 있었습니다.

 

오늘 알아볼 것은 이런 단순하게 상태에 대한 정의를 넘어서 더 다양한 방식들을 사용해보려고 합니다.

 

연관값

열거형의 case는 각 상태와 연관된 값을 추가로 설정할 수 있습니다. 이를 통해서 해당 case를 더 구체화 할 수 있습니다.

enum NetworkResult {
    case success
    case serverError(code: Int)
}

var someEnum: NetworkResult = .serverError(code: 119)
print(someEnum)
// someEnum1(code: 119)

위 예시와 같이 서버에러의 경우 에러와 연관된 에러코드와 함께 지정되어 해당 케이스의 값을 더 구체적으로 설정할 수 있습니다.

 

CaseIterable protocol

열거형에 CaseIterable을 채택하여 모든 케이스를 배열에 담아 리턴할 수 있습니다.

enum Configuration: CaseIterable {
    case setting
    case ui
    case network
    case logic
}


for element in Configuration.allCases {
    print(element)
}
// setting
// ui
// network
// logic

print(Configuration.allCases.count)
// 4

CaseIterable프로토콜의 allCases는 static 변수라 인스턴스가 아닌 타입으로 접근해야합니다.

public protocol CaseIterable {

    /// A type that can represent a collection of all values of this type.
    associatedtype AllCases : Collection = [Self] where Self == Self.AllCases.Element

    /// A collection of all values of this type.
    static var allCases: Self.AllCases { get }
}

 

 

생성자

열거형은 구조체와 같이 값타입 형식입니다. 그래서 생성자를 구현하지 않아도 생성할 수 있지만, 구조체처럼 생성자를 구현할 수 있습니다.

enum GameState: Int, CaseIterable {
    case play = 1
    case record
    case exit
    case none
    
    init(_ index: Int) {
        self = GameState.allCases[index]
    }
}

var baseballGameState1: GameState = .play
var baseballGameState2 = GameState(rawValue: 1)
var baseballGameState3 = GameState(3)
print(GameState.allCases)
print(baseballGameState3)
// 4
// none

GameState(3) 인덱스를 받아서 생성할 수 있습니다.

이렇게 생성자를 사용한다면 코드를 더 간결하게 사용 할 수 있을것이라 생각합니다.

 

 

프로퍼티와 메서드

열거형 안에 프로퍼티와 메서드를 구현할 수 있습니다. 하지만 get-only 프로퍼티만 구현할 수 있습니다.

enum SomeEnum2 {
    case some1
    case some2
    
    var description: String {
        switch self {
        case .some1:
            return "some1"
        case .some2:
            return "some2"
        }
    }
    func printState(state: SomeEnum2) {
        switch self {
        case .some1:
            print("some1")
        case .some2:
            print("some2")
        }
    }
}

var someEnum2: SomeEnum2 = .some1
print(someEnum2.description)
// some1

 

 

static

열거형 안에 static 키워드를 추가하여 타입메서드와 타입 프로퍼티를 생성할 수 있습니다.

enum SomeEnumWithStaticMethod {
    case some1
    case some2
    
    static var count: Int = 0
    
    static func printState(state: SomeEnumWithStaticMethod) {
        switch state {
        case .some1:
            print("some1")
        case .some2:
            print("some2")
        }
    }
}
print(SomeEnumWithStaticMethod.count)
SomeEnumWithStaticMethod.count = 10
SomeEnumWithStaticMethod.printState(state: .some1)
SomeEnumWithStaticMethod.printState(state: .some2)
print(SomeEnumWithStaticMethod.count)
// 0
// some1
// some2
// 10

기존의 프로퍼티는 get-only 프로퍼티만 생성할 수 있지만, static 키워드로 타입프로퍼티를 선언하면 타입에 접근하기 때문에 get-set property로 선언할 수 있습니다. 해당 타입 메서드와 프로퍼티는 값에 접근할 때 타입에 접근하여 사용해야합니다.

이를 통해 UI의 환경설정같은 상수를 선언할 때 자주 사용합니다.

enum AppStyles {
    enum Colors {
        static let mainColor = UIColor(red: 1, green: 0.2, blue: 0.2, alpha: 1)
        static let darkAccent = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
    }
    enum FontSizes {
        static let small: CGFloat = 12
        static let medium: CGFloat = 14
        static let large: CGFloat = 18
        static let xlarge: CGFloat = 21
    }
}

AppStyles.Colors.mainColor
AppStyles.FontSizes.medium

 

 

상태를 변화하는 메서드

값타입 열거형은 자신의 상태를 변경시키는 메서드도 구현할 수 있습니다.

값이 변하는 메서드라면 구조체처럼 메서드앞에 mutating 키워드를 사용합니다.

enum Toggle {
    case on
    case off
    
    mutating func toggle() {
        switch self {
        case .on:
            self = .off
        case .off:
            self = .on
        }
    }
}

var buttonState: Toggle = .off
buttonState.toggle()

 

 

If case, guard case

열거형 안의 case에 접근할 때 switch구문을 통해 접근했었습니다.

다만 switch구문을 사용하면 모든 케이스에 대한 동작을 구현해야 하기 때문에 코드가 길어질 수 밖에없습니다.

여러 케이스중 하나에만 접근하고 싶다면 switch 대신 If case, guard case를 사용하면 더 간결하게 열거형에 접근할 수 있습니다.

if case .on = buttonState {
    print("On")
}

if case .on = buttonState {
    print("ON")
}
 On
 ON

guard case .on = buttonState else {
    return
}

 

 

프로토콜 채택

열거형에 프로토콜을 채택할 수 있습니다.

protocol GameStateProtocol {
    static var gameStateTitle: String { get }
    func printGameState()
    mutating func changeGameState(state: BaseballGameState)
}

enum BaseballGameState: String, GameStateProtocol {
    
    static var gameStateTitle: String = "Baseball"
    
    case play = "play"
    case pause = "pause"
    case exit = "exit"
    
    func printGameState() {
        print(BaseballGameState.gameStateTitle)
    }
    
    mutating func changeGameState(state: BaseballGameState) {
        self = state
    }
    
}

var state = BaseballGameState.pause
state.printGameState()
print(BaseballGameState.gameStateTitle)
state.changeGameState(state: .play)
print(state)
// Baseball
// Baseball
// play

프로토콜 채택시 프로토콜의 요구사항을 구현하고 열거형을 사용할 수 있습니다.

 

 

이렇게 열거형의 더 많은 사용방법을 알아보았습니다.

이러한 방법들을 활용하면 더 간결하고 가독성 있게 코드를 작성할 수 있을것이라 생각합니다

'Swift > 문법' 카테고리의 다른 글

ARC dive deep  (0) 2025.03.21
후행 클로저  (0) 2025.03.21
Swift Protocols  (0) 2025.03.11
Swift 구조체와 클래스  (0) 2025.02.12
Swift Concurrency 정리  (7) 2024.11.10

+ Recent posts