TCA는 Pointfreeco에서 Redux 아키텍처를 참고하여 개발한 단방향 아키텍쳐로, Store 내부에 State, Action, Reducer를 두어 상태 변경에 대한 단방향 흐름을 유지하여, 상태 관리 간의 일관성을 유지하게 해준다.
아키텍쳐의 전체적인 흐름은 아래와 같다.
구버전의 TCA에서는 아래와 같이 Reducer에서 Environment를 클로저 타입의 변수로 선언하였다.
let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, environment in
// ...
}
최신 버전의 TCA에서는 Reducer 함수에서 Environment를 인자로 받지 않는다. 그러므로 필수적으로 구현하지 않아도 되며 필요 시 추가할 수 있는 방법이 있는데, 해당 방법에 대해서는 아래 포스트에서 설명하고 있다.
아래는 이해하기 쉽게 작성한 TCA 예제 코드이다. (SwiftUI)
import SwiftUI
import ComposableArchitecture
struct ExampleView: View {
@Bindable var store: StoreOf<ExampleReducer>
var body: some View {
VStack {
Text(self.store.count)
Button(action: {
self.store.send(.increaseButtonTapped)
}) {
Text("Increase Count")
}
}
}
}
struct ExampleReducer : Reducer {
@ObservableState
struct State: Equatable {
var count : Int = 0
}
@CasePathable
enum Action: BindableAction {
case binding(BindingAction<State>)
case increaseButtonTapped
}
var body: some Reducer<State, Action> {
@BindingReducer()
Reduce { state, action in
switch action {
case .binding :
return .none
case .increaseButtonTapped :
state.count += 1
return .none
}
}
}
}
#Preview {
ExampleView(store: Store(initialState: ExampleReducer.State(), reducer: ExampleReducer.init))
}
ExampleReducer 에는 상태를 관리하는 State, 사용자의 Action, 그리고 Action에 따라 State를 변경하는 함수부인 Reducer 로 이루어진 모습을 확인할 수 있다.
또한 View에는 store 를 @Binding PropertyWrapper 로 정의하여 사용한다.
이전 버전의 TCA에서는 이러한 @Binding PropertyWrapper 없이 전체 뷰를 WithViewStore로 감싸 바인딩의 기능을 수행해주었는데, 지금은 Deprecate 된 상태이다.