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 |