HIT해
[iOS/Swift] Generic enum allCases 사용하기 본문
재사용성 컴포넌트를 구현하기위해 단순히 String이나 Int같이 기본 자료형을 사용해도 괜찮지만.
이미 만들어둔 Enum을 CaseIterable을 활용하여 배열로 만들고 그걸 컴포넌트에 사용한다면 유지보수성과 가독성 모두 향상된다.
왜냐하면 선언한 열거형 내부 case들만 바꿔주면 모든 곳에서 일관성있게 또 간편하게 사용할 수 있기 때문이다.
내가 만들려고 했던 디자인이다.
배경 소품 얼굴은 다르지만 아래 컴포넌트는 동일한 디자인이기에 재사용 가능한 컴포넌트를 만든다면 탭이 몇개가 늘어도 금방 만들 수 있을 것이다.
1. Generic 프로토콜 선언
열거형들에 공통된 프로토콜을 만들어 제네릭으로 똑같은 프로토콜을 가지고 있는 값들만 들어올 수 있게 만들어주었다.
protocol Customizable : Hashable, CaseIterable{
var description: String { get }
}
enum Face : String, Customizable {
case sparkle = "반짝이"
case sosim = "소심이"
case saechim = "새침이"
case nareun = "나른이"
case meong = "멍이"
case cupid = "큐피드"
case bboombboom = "뿜뿜이"
case balral = "발랄이"
case chic = "시크"
var description: String {
return self.rawValue
}
}
// 배경
enum Background : String,Customizable {
case July = "7월의 푸른 잔디"
case April = "4월의 분홍빛 노을"
var description: String {
return self.rawValue
}
}
이제 위의 열거형들을 활용하는 컴포넌트를 만들어보자
struct CustomizeView<T: Customizable>: View where T.AllCases == [T] {
let store: StoreOf<HomeFeature>
private let columns = [
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
ScrollView(.horizontal) {
LazyHGrid(rows: columns, spacing: 20) {
ForEach(T.allCases, id: \.self) { item in
Button(action: {
}) {
VStack(spacing: 15) {
Text("\(item.description)")
.font(Font.customFont(Font.caption1))
.foregroundColor(Color.decoSheetTextColor)
.padding(.bottom, 5)
}
where T.AllCases == [T] 는 T가 AllCases 프로퍼티를 가지고 그 타입이 [T] 배열과 같다는 것을 보장한다.
이러한 명시가 없다면 T.allCases를 사용할 수 없다.
추가로 이러한 방법으로 에러가 발생해 T.allCases를 Array로 감싸보기도 했지만 해결되지 않았다.
그 이유는 프로토콜이 AllCases의 타입을 명시적으로 정의하지 않고 있어 컴파일러가 이 프로퍼티를 제대로 인식하지 못했기 떄문이다.
protocol Customizable : Hashable, CaseIterable where AllCases == Array<Self>{
var description: String { get }
}
이렇게 만들면 타입이 다르지만 Customizeable 프로토콜을 상속하고 있는 열거형들만 컴포넌트에 들어 올 수 있고 AllCases를 사용할 수 있게 된다.
+ 번외
버튼을 눌렀을때 다른 동작을 하고 해당 동작을 수행하기 위해서는 일치하는 타입을 넣어주어야했는데 수행해야하는 함수는 enum 안에 넣어주었다.
protocol Customizable : Hashable, CaseIterable where AllCases == Array<Self>{
var description: String { get }
func performAction(with store: StoreOf<HomeFeature>)
}
// 표정
enum Face : String, Customizable {
func performAction(with store: ComposableArchitecture.StoreOf<HomeFeature>) {
store.send(.selectFace(self))
}
// 사용할 때
Button(action: {
item.performAction(with: store)
})
플러터에 이어서 Swift로 직접 제네릭을 활용해서 구현해보았다.
점점 재사용성 컴포넌트를 개발하는데에 있어 즐거움이 느껴지기 시작한다. (뭔가 위험한것같기도.)
'Swift > UIKit 개발 노트' 카테고리의 다른 글
[iOS/SceneKit] 다른 모델의 노드를 추가해보자 (0) | 2024.08.26 |
---|---|
[iOS/SwiftUI] UI가 마음대로 움직여요. (1) | 2024.08.25 |
[iOS/SwiftUI] Picker (0) | 2024.08.23 |
[iOS/SceneKit] 모델 교체하기 (0) | 2024.08.22 |
[iOS/SwiftUI] TabView (0) | 2024.08.22 |