[Swift] CaseIterable Protocol을 이용해 Enum cases 반복하기

안녕하세요. 미닛메이드 Minnit 입니다😌

오랜만이네요..ㅎㅎ

여러 코드들을 참고하다가 enum 옆에

CaseIterable 가 많이 눈에 들어오더라구요?

 

그래서 CaseIterable의 개념과 어느 경우에 사용될 수 있는지를 알아보도록 하겠습니다 !

 


CaseIterable  뭘까요?

Apple🍎의 공식 문서를 확인해보면

Protocol로 "모든 값들의 collection을 제공한다" 라고 하네요!

Swift에서는 Collection Type으로 3가지를 제공해주고 있는데

바로 Array, Set, Dictionary 가 있죠??

 

Collection을 제공한다 라는 말은

Collection Type처럼 사용할 수 있게 도와준다 라는 말로 들리네요!

더 자세히 말하면 CaseIterable를 선언 시 사용할 수 있는

allCases가 Collection Type으로 되어있기 때문이죠 ⭕️

static var allCases: Self.AllCases { get }
associatedtype AllCases : Collection = [Self] where Self == Self.AllCases.Element

 

그래서 이 CaseIterable을 enum에 사용하면

Collection Type에서 사용하는 다양한 메소드(ex. map, count 등)를 사용할 수 있게 됩니다

그래서 enum에 존재하는 모든 case들을 나열 혹은 반복하고 싶을 때 주로 사용하게 됩니다 😊

 

예시를 들어볼게요 💬

enum FontStyle: CaseIterable {
    case title
    case subtitle
    case sectionTitle
    case body
    case comment
}

let enumCount = FontStyle.allCases.count
let enumCaseName = FontStyle.allCases.map { "\($0) 스타일" }.joined(separator: ", ")

// enumCount: 5
// enumCaseName: title 스타일, subtitle 스타일, sectionTitle 스타일, body 스타일, comment 스타일

이런 식으로 CaseIterable을 넣으면 생기는 allCases property를 이용하여 

count로 case의 개수를 셀 수도 있고, map과 같은 고차 함수도 사용할 수 있네요

오호라.. 이거 잘 쓰면 편리하겠는데요 ? 

 

CaseIterable를 어떻게 활용하면 좋을까 해서 조금 더 찾아봤습니다 !

enum TextType: CaseIterable {
    case title
    case subtitle
    case sectionTitle
    case body
    case comment
}

var fonts = [TextType : UIFont]()

for type in TextType.allCases {
    switch type {
    case .title:
        fonts[type] = .preferredFont(forTextStyle: .headline)
    case .subtitle:
        fonts[type] = .preferredFont(forTextStyle: .subheadline)
    case .sectionTitle:
        fonts[type] = .preferredFont(forTextStyle: .title2)
    case .body:
        fonts[type] = .preferredFont(forTextStyle: .body)
    case .comment:
        fonts[type] = .preferredFont(forTextStyle: .footnote)
    }
}

enum의 case으로 Font 유형을 정의한 후 CaseIterable를 사용하는 경우인데요,

allCases를 이용해서 모든 케이스를 돌면서 특정 case마다 폰트 타입을 정의했습니다

그러면 ~~~

let nameLabel: UILabel = UILabel()
nameLabel.text = "이름표"
nameLabel.font = fonts[.title]

이렇게 label의 font에 활용을 할 수 있습니다 !

저는 UIFont Extension을 따로 만들어서 font를 설정하는 방식을 주로 사용했는데

이런 방법도 사용할 수 있겠네요 😊

 

그럼 모든 enum에 CaseIterable를 사용할 수 있을까요?

공식문서 하단을 보면

The compiler can automatically provide an implementation of the CaseIterable  requirements for any enumeration without associated values or @available  attributes on its cases. The synthesized allCases  collection provides the cases in order of their declaration.

"컴파일러는 자동으로 연관 값이나 @available 속성이 없는 모든 열거에 대한

CaseIterable 요구 사항의 구현을 제공할 수 있다."

라는 말은 즉 @availble 속성이 있거나 연관 값(Associated Value)이 있다면

CaseIterable을 사용할 수 없다는 말이 되겠쥬 ?

 

이 말이 공식 문서에 2번이나 언급되어 있어서

조금 더 자세히 보고 갈게요 !

 

먼저 Associated Value 바로 연관 값입니다 !

연관 값은 열거형에 첨가된 추가 정보로 

enum FontStyle {
    case title(Int)
    case subtitle(Int)
    case sectionTitle
    case body
    case comment
}

case 중 title과 subtitle case는 (Int)의 연관 값을 가지고 있는 상태입니다 !

Raw Values와는 다른 개념인 거 아시죠?ㅎㅎ

 

정말 연관 값이나 @availble 속성이 있으면 CaseIterable를 사용할 수 없는 걸까요 ?

바로 테스트를 해봤습니다

"Type 'FontStyle' does not conform to protocol 'CaseIterable'

Do you want to add protocol stubs?"

라는 에러가 나오는 것을 볼 수 있습니다

( 못 믿어서 미안해요... )

 

연관 값이나 @availble 속성이 있는 enum에서 CaseIterable를 사용할 수 없으니 대체할 거냐

즉, allCases를 직접 정의할 것이냐를 물어보고 있네요 !

 

직접 구현해서 사용한다면 이렇게 연관 값이 있는 case는

직접 기본값을 넣어서 구현을 해주면 됩니다 !

enum FontStyle: CaseIterable {    
    static var allCases: [FontStyle] {
        return [title(0), subtitle(0), sectionTitle, body, comment]
    }
    
    case title(Int)
    case subtitle(Int)
    case sectionTitle
    case body
    case comment
}

 


이렇게 CaseIterable의 개념과 사용을 알아보았습니다

오늘도 공식문서에 대한 신뢰감을 쌓고 가네요 !

 

혹시 틀린 점 있거나 더 궁금한 점이 있다면

댓글로 남겨주세요 ⭐️

오늘도 감사합니다 :)

 

 

 

[참고 링크]

 

Apple Developer Documentation

 

developer.apple.com

 

Enum with associated value does not conform to CaseIterable and throws error

The following enum works fine without any error. enum EnumOptions: CaseIterable { case none case mild case moderate case severe case unmeasurable } When I ...

stackoverflow.com

 

Enum iterations in Swift | Swift by Sundell

With each new release, Swift keeps getting better and better at creating compiler-generated implementations of common boilerplate. With things like Codable and synthesized conformances to Equatable and Hashable, we can now leverage the compiler to use its

www.swiftbysundell.com