# TCA

## iOS 17 아래 버전

@Bindable 은 iOS 17 이후로 제공

@ObservableState 를 사용하기 위해서는 @Perception.Bindable 로 사용해야 한다.

추가로 WithPerceptionTracking 를 해야 정상 동작한다.

Action에도 @CasePathable 를 추가해야 한다.

```swift
struct RootView: View {
  
  @Perception.Bindable
  private var store: StoreOf<RootFeature>
  
  init(store: StoreOf<RootFeature>) {
    self.store = store
  }
  
  var body: some View {
    WithPerceptionTracking {
      TabView(selection: $store.selectedTab.sending(\.tabSelected)) {
        HomeView(store: store.scope(state: \.home, action: \.home))
          .tabItem { Text(RootTab.home.title) }
          .tag(RootTab.home)
        
        FeedView(store: store.scope(state: \.feed, action: \.feed))
          .tabItem { Text(RootTab.feed.title) }
          .tag(RootTab.feed)
        
        TodayView(store: store.scope(state: \.today, action: \.today))
          .tabItem { Text(RootTab.today.title) }
          .tag(RootTab.today)
        
        MyView(store: store.scope(state: \.my, action: \.my))
          .tabItem { Text(RootTab.my.title) }
          .tag(RootTab.my)
      }
    }
  }
  
}

@Reducer
struct RootFeature {
  
  @ObservableState
  struct State: Equatable {
    var selectedTab: RootTab = .home
    var home: HomeFeature.State = .init()
    var feed: FeedFeature.State = .init()
    var today: TodayFeature.State = .init()
    var my: MyFeature.State = .init()
  }
  
  @CasePathable
  enum Action {
    case home(HomeFeature.Action)
    case feed(FeedFeature.Action)
    case today(TodayFeature.Action)
    case my(MyFeature.Action)
    
    case tabSelected(RootTab)
  }
  
  var body: some ReducerOf<Self> {
    Scope(state: \.home, action: \.home) {
      HomeFeature()
    }
    
    Scope(state: \.feed, action: \.feed) {
      FeedFeature()
    }
    
    Scope(state: \.today, action: \.today) {
      TodayFeature()
    }
    
    Scope(state: \.my, action: \.my) {
      MyFeature()
    }
    
    Reduce { state, action in
      switch action {
      case .home, .feed, .today, .my:
        return .none
        
      case let .tabSelected(selectedTab):
        state.selectedTab = selectedTab
        return .none
      }
    }
  }
  
}

```

### GeometryReader 내부도 WithPerceptionTracking로 감싸야 한다

외부 뿐만 아니라 GeometryReader를 사용하는 경우 내부에도 감싸야 함

```swift
WithPerceptionTracking {
  VStack {
    GeometryReader { proxy in
      WithPerceptionTracking {
        Path { path in
          path.move(to: CGPoint(x: proxy.size.width / 2, y: proxy.size.height / 2))
          path.addLine(to: CGPoint(x: proxy.size.width / 2, y: 0))
        }
        .stroke(.primary, lineWidth: 3)
        .rotationEffect(.degrees(Double(store.secondsElapsed) * 360 / 60))
      }
    }
  }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hogumachu-docs.gitbook.io/playground/troubleshooting/tca.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
