iOS/TCA

TCA Binding

dely 2024. 3. 5. 22:16

 

강의를 듣다가 새로운 것을 발견하여 기록한다.

기존에 TCA 아키텍처 기반으로 TextField의 text를 받아올 때 다음과 같이 사용했었다.

 

1)

import SwiftUI
import ComposableArchitecture

struct SomethingView: View {
    let store: StoreOf<SomethingFeature>
    @State var email: String = ""

    var body: some View {
        WithViewStore(self.store, observe: { $0 }) { viewStore in
            VStack {
                TextField("이메일을 입력하세요", text: $email)
                .padding()
                .onChange(of: email) {
                    viewStore.send(.textChanged($0))
                }
                Text(viewStore.email)
            }
        }
    }
}
import ComposableArchitecture

struct SomethingFeature: Reducer {
    struct State: Equatable {
        var email: String = ""
    }

    enum Action: Equatable {
        case textChanged(String)
    }

    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
                case .textChanged(let email):
                    state.email = email
                    return .none
            }
        }
    }
}

 

email의 상태값을 onChange로 TCA Action에 viewStore.send 해서 사용을 했었다.

 

 

그런데 찾다보니 다음 방식으로도 쓸 수 있다는 것을 알았다.

viewStore.bindint(get: send:) 방식이다.

 

2)

struct SomethingView: View {
    let store: StoreOf<SomethingFeature>
    @State var email: String = ""

    var body: some View {
        WithViewStore(self.store, observe: { $0 }) { viewStore in
            VStack {
                TextField("이메일을 입력하세요", text: viewStore.binding(get: \\.email, send:  { .textChanged($0) }))
                .padding()
                Text(viewStore.email)
            }
        }
    }
}

 

 

그리고.. 강의를 듣다가 또 새로운 방법을 알게 되었다.

 

TCA 1.0에서 "BindingReducer"라는 것이 나왔고,

State 내 변수에 @BindingState 를 붙여주고,

Action에 BindableAction 프로토콜을 채택하여 binding(BindingAction<State>) case를 넣어주고,

Reducer에 BindingReducer() 를 넣어줌으로써

 

View 코드에서 viewStore.$email 로 접근하여 더 간편하게 쓸 수 있다.

 

3)

struct SomethingView: View {
    let store: StoreOf<SomethingFeature>
    @State var email: String = ""

    var body: some View {
        WithViewStore(self.store, observe: { $0 }) { viewStore in
            VStack {
                TextField("이메일을 입력하세요", text: viewStore.$email)
                .padding()
                Text(viewStore.email)
            }
        }
    }
}
import ComposableArchitecture

struct SomethingFeature: Reducer {
    struct State: Equatable {
        @BindingState var email: String = ""
    }

    enum Action: Equatable, BindableAction {
        case binding(BindingAction<State>)
    }

    var body: some ReducerOf<Self> {
        BindingReducer()
        Reduce { state, action in
            switch action {
            case .binding(_):
                    return .none
            }
        }
    }
}

 

 

위의 1), 2), 3) 모두 동일하게 동작하는 것을 볼 수 있다.

 

반응형