SwiftUI는 @State , @ObservedObject 등 프로퍼티 래퍼를 통해 양방향 바인딩을 구현하고, State 관리를 간편하게 할 수 있는 기능을 제공한다. 추가적으로 Binding은 @State 프로퍼티 래퍼와 함께 View간 상호 작용을 위해 사용되고 이는 양방향 통신을 가능하게 한다.
Binding을 사용하므로써 State 변화를 UI에 즉시 반영하면서 코드 작성이 간결하다는 장점이 있지만 State 관리가 복잡해질수록 변화에 따른 Side Effects를 관리하는데 어려움이 있다. 이런 이유로 전체 비즈니스 로직을 담당하고 내부 State 변화를 관리하는 TCA에서는 SwiftUI의 기본 @Binding 래퍼로 State를 관리하기에 적합하지 않을 수 있다.
이러한 문제를 해결하기 위해 TCA에서는 State의 변화는 Action을 전송하고 Reducer가 이를 수신하여 로직을 처리한다. 이로써 State 변화에 따른 Side Effects를 일관되게 관리하고 State 변화 로직을 명확하게 파악할 수 있어 복잡한 State 관리가 가능해 진다.
하지만 간단한 State 변화에 비해 코드가 복잡하고 각 State 변화에 대해 Action을 정의하고 Reducer에서 이를 처리하는 로직을 작성해야한다.
TCA Binding
TCA의 가장 기본적인 바인딩은 Binding(get: send: ) 로 두개의 클로저를 매개변수로 받는다.
여기서 get은 State를 바인딩의 값으로 변환하도록 하는 클로저 역할을 하고 send는 바인딩의 값을 다시 Store에 피드백 하는 Action으로 변환하는 클로저의 역할을 한다.
간단한 예제로 살펴보자
struct Settings: Reducer {
struct State: Equatable {
var isHapticFeedbackEnabled = true
}
}
예제에서 Reducer는 사용자가 햅틱 피드백을 활성화 했는지 추적하는 도메인 기능이 있다고 가정한다. State에는 햅틱 피드백에 대한 Bool 프로퍼티를 정의한다.
struct Settings: Reducer {
struct State: Equatable { /* code */ }
enum Action {
case isHapticFeedbackEnabledChanged(Bool)
/* code */
}
/* code */
}
만약 View에서 토글을 사용해 Store 외부에서 이 State를 조정할 수 있다면 Action의 형태를 정의할 수 있게 된다.
struct Settings: Reducer {
struct State: Equatable { /* code */ }
enum Action { /* code */ }
func reduce(
into state: inout State, action: Action
) -> Effect<Action> {
switch action {
case let .isHapticFeedbackEnabledChanged(isEnabled):
state.isHapticFeedbackEnabled = isEnabled
return .none
/* code */
}
}
}
이제 reducer에서 해당 Action을 조작할 수 있도록 하면 reducer에서 Action에 대한 결과로 State의 값이 변경된다.
struct SettingsView: View {
let store: StoreOf<Settings>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
Form {
Toggle(
"Haptic feedback",
isOn: viewStore.binding(
get: \\.isHapticFeedbackEnabled,
send: { .isHapticFeedbackEnabledChanged($0) }
)
)
/* code */
}
}
}
}
이 내용을 토대로 View에 적용하면 SwiftUI UI 컨트롤의 이벤트를 단방향 통신으로 TCA에 반양할 수 있는 바인딩을 완료하게 된다 🙂
오늘은 이렇게 TCA에서 Binding을 통해 이벤트가 단방향으로 전달되는 과정에 대한 간단한 개념만 이해해보았다 ㅎㅎ 다음 시간에는 이 내용을 바탕으로 간단하게 구현해보면서 기초를 다져보자
오늘은 여기까지!
'◽️ Programming > SwiftUI' 카테고리의 다른 글
SwiftUI와 UIKit의 다른 생명주기 방식 (0) | 2024.10.22 |
---|---|
SwiftUI TCA (2) - Effect (0) | 2024.09.15 |
SwiftUI TCA (1) (0) | 2024.09.09 |
[SwiftUI] onChange 역할을 알아보자 (0) | 2024.08.09 |
[SwiftUI] @StateObject , @ObservedObject, @EnviromentObject의 역할과 차이점을 자세하게 알아보자 (1) | 2024.07.23 |