ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TCA 1.0 The Basic
    iOS/TCA 2024. 3. 3. 00:23

    TCA(The Composable Architecture) 1.0을 정리해보자.

    (글을 쓰고 있는 지금 현재 TCA 버전 1.9.1이 최신 버전이라는 것은 비밀..😱)

    일단 TCA는 선언형 UI인 SwiftUI와 잘 맞는 아키텍처로 알려져있고, 또 단방향 Flow라는 특징을 가지고 있다.

    간략하게 설명하면 View에서 어떤 action이 들어오면 TCA Store 내부 Action에서 State를 변경 시켜주고,

    그 변경된 State를 SwiftUI View에서 감지하여 화면이 업데이트 되는 방식이다.

    (대충 위의 그림과 같은 데이터의 방향성을 가진다. (출처: https://www.merowing.info/multi-store-tca/))

    기존 1.0 버전 이전에서는 State, Action, Reducer 구조체를 각각 따로 만들었는데,

    1.0부터 Reducer Protocol을 준수하는 방법으로 개선되었다.

    Reducer Protocol에는 State 구조체, Action 열거형, Reducer function이 포함되어 있고,

    각 내용을 작성해주기만 하면 된다.

    import ComposableArchitecture
    
    struct SomethingFeature: Reducer {
        struct State: Equatable {
            var name: String = ""
            var age: Int = 0
        }
    
        enum Action: Equatable {
            case nameButtonTapped
            case ageButtonTapped
        }
    
        var body: some ReducerOf<Self> {
            Reduce { state, action in
                switch action {
                    case .nameButtonTapped:
                        state.name = "hello Mr.L"
                        return .none
                    case .ageButtonTapped:
                        state.age += 1
                        return .none
                }
            }
        }
    }
    
    
    struct SomethingView {
    	...
    }

    주로 위의 코드와 같이 Reducer를 채택한 struct 하나와

    SwiftUI View를 쌍으로 개발하게 된다. (ex, SomethingFeature, SomethingView)

    Reducer body안에서 return .none은 Effect가 따로 없다는 뜻인데,

    Effect로는 타이머, API 호출, 다른 Action 호출 등 여러가지를 사용할 수 있다.

    위의 예제는 간단하게 나타낸 것으로, 따로 Effect를 만들지는 않았다.

    import SwiftUI
    import ComposableArchitecture
    
    struct SomethingView: View {
        let store: StoreOf<SomethingFeature>
    
        var body: some View {
            WithViewStore(self.store, observe: { $0 }) { viewStore in
                VStack {
                    HStack {
                        Text("\(viewStore.name)")
                        Button("Name Button") {
                            viewStore.send(.nameButtonTapped)
                        }
                    }
                    HStack {
                        Text("\(viewStore.age)")
                        Button("Age Button") {
                            viewStore.send(.ageButtonTapped)
                        }
                    }
                }
            }
        }
    }

    View에서는 위와 같이 화면을 구성해보았는데,

    let store: StoreOf<SomethingFeature>를 선언하고, store를 바로 접근하여 쓰는 것이 아니라

    WithViewStore을 두어서 viewStore로 접근해야한다.

    (그렇지 않으면 매우 느린 동작을 볼 수 있다고 한다..)

    그러나 iOS 17이상이라면 store에 직접 접근 가능하다고 한다.

    SwiftUI가 내부적으로 어떤 상태인지 정확하게 파악 가능하기 때문이라고 하는데 이건 나중에 직접 살펴봐야될 것 같다.

    추가적으로, #Preview에서

    위에 만든 Store를 초기화해줄 때 ._printChanges()를 해주면 버튼을 눌러 동작할 때마다 상태값이 어떻게 바뀌는지

    State가 콘솔에 찍혀서 볼 수 있다.

    #Preview {
        SomethingView(
            store: Store(initialState: SomethingFeature.State()) {
                SomethingFeature()
                    ._printChanges()
            }
        )
    }

    • 버튼 눌렀을 때 보이는 콘솔

    received action:
      SomethingFeature.Action.nameButtonTapped
      SomethingFeature.State(
    -   name: "",
    +   name: "hello Mr.L",
        age: 0
      )
    
    received action:
      SomethingFeature.Action.ageButtonTapped
      SomethingFeature.State(
        name: "hello Mr.L",
    -   age: 0
    +   age: 1
      )
    
    received action:
      SomethingFeature.Action.ageButtonTapped
      SomethingFeature.State(
        name: "hello Mr.L",
    -   age: 1
    +   age: 2
      )

    • 시뮬레이터 실행


    Uploaded by N2T

    반응형

    'iOS > TCA' 카테고리의 다른 글

    TCA Binding  (0) 2024.03.05
    TCA Toast  (0) 2024.03.04

    댓글

Designed by Tistory.