Taehyeon-Kim / Taehyeon-Kim.github.io

The life of recording is unbreakable
https://taehyeon-kim.github.io
MIT License
1 stars 0 forks source link

TCA101: 공식문서 예제를 통해 TCA 기본 사용법 알아보기(last updated: v1.4.2) #15

Open Taehyeon-Kim opened 10 months ago

Taehyeon-Kim commented 10 months ago

State 정의

기능의 상태 타입을 정의해야 한다. (e.g. 알림 문자열, 카운트 등의 변수)

Action 정의

Action은 명백한 동작과 다소 명확하지 않은 동작으로 구분할 수 있다:

  1. 명백한 동작(e.g. 감소 버튼, 증가 버튼, 특정 버튼 탭)
  2. 명확하지 않은 동작(e.g. 알림 해제, API 요청 후 응답을 받을 때 발생하는 동작)
@Reducer
struct Feature {
  enum Action {
    case decrementButtonTapped
    case incrementButtonTapped
    case numberFactButtonTapped
    case factAlertDismissed
    case numberFactResponse(String)
  }
}

Body 정의

실제 로직 수행, Effect를 실행하고 반환 부분을 구현해야 한다. 그리고 현재 상태를 다음 상태로 묘사하는데에 사용된다.

var body: some Reducer<State, Action> {
  Reduce { state, action in
    switch action {
    case .factAlertDismissed:
      state.numberFactAlert = nil
      return .none

    case .decrementButtonTapped:
      state.count -= 1
      return .none

    case .incrementButtonTapped:
      state.count += 1
      return .none

    case .numberFactButtonTapped:
      return .run { [count = state.count] send in
        let (data, _) = try await URLSession.shared.data(
          from: URL(string: "http://numbersapi.com/\(count)/trivia")!
        )
        await send(
          .numberFactResponse(String(decoding: data, as: UTF8.self))
        )
      }

    case let .numberFactResponse(fact):
      state.numberFactAlert = fact
      return .none
    }
  }
}

View 정의

Feature를 정의했다면 View를 정의할 차례이다. View에서는 상태를 관찰할 수 있는 Store 객체를 들고 있어야 한다.

struct FeatureView: View {
  let store: StoreOf<Feature>

  var body: some View {
    WithViewStore(self.store, observe: { $0 }) { viewStore in
      VStack {
        HStack {
          Button("−") { viewStore.send(.decrementButtonTapped) }
          Text("\(viewStore.count)")
          Button("+") { viewStore.send(.incrementButtonTapped) }
        }

        Button("Number fact") { viewStore.send(.numberFactButtonTapped) }
      }
      .alert(
        item: viewStore.binding(
          get: { $0.numberFactAlert.map(FactAlert.init(title:)) },
          send: .factAlertDismissed
        ),
        content: { Alert(title: Text($0.title)) }
      )
    }
  }
}

struct FactAlert: Identifiable {
  var title: String
  var id: String { self.title }
}

Store는 Runtime이라고 했다. State, Action, Reducer를 가지고 있는 친구인데 Store를 실제로 관찰하거나 Store에 액션을 전송하기 위해서는 ViewStore로 한 번 감싸져야 한다. ViewStore가 ObservableObject이기 때문이다. Store를 ViewStore로 변환해주는 역할을 하는 것이 WithViewStore 구조체이다.