[TCA] OptionalState (IfLetCase)
목차
- OptionalState란?
- OptionalState 예제
- IfLetCase 알아보기
- OptionalState란?
Reducer의 State 중 optional로 선언된 state를 일컬음.
- OptionalState 예제
1. optional값은 IfLetCase를 통해서 분기처리 가능
- 자세한 사항을 코드의 주석 참고
// MARK: - Feature domain
struct OptionalBasics: Reducer {
struct State: Equatable {
var optionalCounter: Counter.State? // 1. ✅ State를 optional 상태로 보유
}
enum Action: Equatable {
case optionalCounter(Counter.Action)
case toggleCounterButtonTapped
}
var body: some Reducer<State, Action> {
Reduce { state, action in
switch action {
case .toggleCounterButtonTapped:
state.optionalCounter = state.optionalCounter == nil
? Counter.State()
: nil
return .none
case .optionalCounter:
return .none
}
}
.ifLet(\.optionalCounter, action: /Action.optionalCounter) {
// 2. ✅ Counter 리듀서를 OptionalBasics 리듀서와 통합
Counter()
}
}
}
// MARK: - Feature view
struct OptionalBasicsView: View {
let store: StoreOf<OptionalBasics>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
Form {
Section {
AboutView(readMe: readMe)
}
Button("Toggle counter state") {
viewStore.send(.toggleCounterButtonTapped)
}
// 3. ✅ View에서 IfLetStore로 optional 값을 분기
IfLetStore(
self.store.scope(
state: \.optionalCounter, // 3-1. ✅ optional state값 연결 (type: Counter.State?)
action: OptionalBasics.Action.optionalCounter // 3-2. ✅ action 연결 (type: Counter.Action)
),
then: { store in // 3-3. ✅ store (type: store: Store<Counter.State, Counter.Action>)
Text(template: "`CounterState` is non-`nil`")
CounterView(store: store)
.buttonStyle(.borderless)
.frame(maxWidth: .infinity)
},
else: {
// 3-4. ✅ state가 nil인 경우
Text(template: "`CounterState` is `nil`")
}
)
}
}
.navigationTitle("Optional state")
}
}
// MARK: - SwiftUI previews
struct OptionalBasicsView_Previews: PreviewProvider {
static var previews: some View {
Group {
NavigationView {
OptionalBasicsView(
store: Store(initialState: OptionalBasics.State()) {
OptionalBasics()
}
)
}
NavigationView {
OptionalBasicsView(
store: Store(
initialState: OptionalBasics.State(optionalCounter: Counter.State(count: 42))
) {
OptionalBasics()
}
)
}
}
}
}
// MARK: - Counter.swift
struct Counter: Reducer {
struct State: Equatable {
var count = 0
}
enum Action: Equatable {
case decrementButtonTapped
case incrementButtonTapped
}
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .decrementButtonTapped:
state.count -= 1
return .none
case .incrementButtonTapped:
state.count += 1
return .none
}
}
}
// MARK: - Feature view
struct CounterView: View {
let store: StoreOf<Counter>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
HStack {
Button {
viewStore.send(.decrementButtonTapped)
} label: {
Image(systemName: "minus")
}
Text("\(viewStore.count)")
.monospacedDigit()
Button {
viewStore.send(.incrementButtonTapped)
} label: {
Image(systemName: "plus")
}
}
}
}
}
- IfLetCase 알아보기
optional state의 unwrap store를 nil과 아닌 값을 분리하여 두개의 뷰를 나누어 보여주는 View
정의
struct IfLetStore<State, Action, Content> where Content : View
예제
IfLetStore(
store.scope(state: \.results, action: Search.Action.results) // 1. ✅ scope 지정
) {
SearchResultsView(store: $0) // 2. ✅ then: nil이 아닌 상태의 뷰
} else: {
Text("Loading search results...") // 2. ✅ else: nil 상태의 뷰
}
구체적 사용 예제
(참고)
'apple > TCA' 카테고리의 다른 글
[TCA] Effect #1 (Basics) (1) | 2023.10.07 |
---|---|
[TCA] SharedState (1) | 2023.09.27 |
[TCA] FocusState (0) | 2023.09.27 |
[TCA] Binding (0) | 2023.09.27 |
[TCA] Tutorial #5 (Multiple presentation destinations) (0) | 2023.09.24 |