> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/pointfreeco/swift-composable-architecture/llms.txt
> Use this file to discover all available pages before exploring further.

# @ObservableState

> A macro that makes reducer state observable for SwiftUI views

## Overview

The `@ObservableState` macro implements SwiftUI's `Observable` protocol for your reducer's state, enabling efficient view updates when state changes. This is the recommended way to make your feature's state observable in SwiftUI.

The macro generates:

* Conformance to Swift's `Observable` and TCA's `ObservableState` protocols
* Internal observation tracking infrastructure (`_$observationRegistrar`, `_$id`, `_$willModify`)
* Automatic property observation for all stored properties
* Integration with TCA's state change notification system

## Basic Usage

Apply `@ObservableState` to your reducer's `State` type:

```swift theme={null}
@Reducer
struct Feature {
  @ObservableState
  struct State {
    var count = 0
    var message = "Hello"
    var isLoading = false
  }
  
  enum Action {
    case incrementTapped
    case setMessage(String)
  }
  
  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .incrementTapped:
        state.count += 1
        return .none
        
      case .setMessage(let text):
        state.message = text
        return .none
      }
    }
  }
}
```

In your SwiftUI view, use `@Bindable` to create bindings:

```swift theme={null}
struct FeatureView: View {
  @Bindable var store: StoreOf<Feature>
  
  var body: some View {
    VStack {
      Text("Count: \(store.count)")
      Button("Increment") {
        store.send(.incrementTapped)
      }
      
      TextField("Message", text: $store.message.sending(\.setMessage))
    }
  }
}
```

## Why Use @ObservableState?

Without `@ObservableState`, you need to manually observe the store using `observe` in your views, which can be verbose and error-prone. The macro provides:

* **Automatic observation**: Views automatically update when observed state changes
* **Fine-grained updates**: Only views observing changed properties re-render
* **SwiftUI integration**: Works seamlessly with SwiftUI's observation system
* **Type safety**: Compile-time guarantees about observable properties

## Controlling Observation

You can control which properties are observed using helper macros:

### @ObservationStateTracked

Explicitly marks a property as tracked (this is the default for all stored properties):

```swift theme={null}
@ObservableState
struct State {
  @ObservationStateTracked
  var importantValue = 0
  
  var count = 0  // Also tracked by default
}
```

### @ObservationStateIgnored

Marks a property to ignore for observation. Use this for properties that shouldn't trigger view updates:

```swift theme={null}
@ObservableState
struct State {
  var displayedCount = 0
  
  @ObservationStateIgnored
  var internalCache: [String: Any] = [:]
  
  @ObservationStateIgnored
  var debugInfo = ""
}
```

Views observing this state won't update when `internalCache` or `debugInfo` change, only when `displayedCount` changes.

## Working with Nested State

When composing features, apply `@ObservableState` to each feature's state:

```swift theme={null}
@Reducer
struct ParentFeature {
  @ObservableState
  struct State {
    var child = ChildFeature.State()
    var title = "Parent"
  }
  
  enum Action {
    case child(ChildFeature.Action)
  }
  
  var body: some ReducerOf<Self> {
    Scope(state: \.child, action: \.child) {
      ChildFeature()
    }
    Reduce { state, action in
      // Parent logic
      return .none
    }
  }
}

@Reducer
struct ChildFeature {
  @ObservableState
  struct State {
    var value = 0
  }
  
  enum Action {
    case increment
  }
  
  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .increment:
        state.value += 1
        return .none
      }
    }
  }
}
```

## Using with @Presents

For presented features, use the `@Presents` macro instead of the `PresentationState` property wrapper:

```swift theme={null}
@Reducer
struct Feature {
  @ObservableState
  struct State {
    @Presents var destination: Destination.State?
    var items: [Item] = []
  }
  
  enum Action {
    case destination(PresentationAction<Destination.Action>)
    case addButtonTapped
  }
  
  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .addButtonTapped:
        state.destination = .addItem(AddItemFeature.State())
        return .none
        
      case .destination:
        return .none
      }
    }
    .ifLet(\.$destination, action: \.destination)
  }
  
  @Reducer
  enum Destination {
    case addItem(AddItemFeature)
    case editItem(EditItemFeature)
  }
}
```

<Note>
  The `@Presents` macro is required when using `@ObservableState` because property wrappers like `PresentationState` are incompatible with Swift's observation system.
</Note>

## Collections and Identified Arrays

The macro works seamlessly with collections:

```swift theme={null}
@ObservableState
struct State {
  var items: IdentifiedArrayOf<Item> = []
  var selectedIDs: Set<Item.ID> = []
  var searchText = ""
}

struct Item: Identifiable {
  let id: UUID
  var name: String
  var isComplete: Bool
}
```

Views observing `items` will update when the array changes.

## Performance Considerations

The `@ObservableState` macro is designed for performance:

* **Structural identity**: Each state value has a unique identifier for efficient change detection
* **Willset notifications**: Observers are notified before mutations occur
* **Minimal overhead**: Only properties actually accessed by views are tracked

## Migration from ViewStore

If you're migrating from the older `ViewStore` pattern:

**Before:**

```swift theme={null}
struct FeatureView: View {
  let store: StoreOf<Feature>
  
  var body: some View {
    WithViewStore(store, observe: { $0 }) { viewStore in
      Text("Count: \(viewStore.count)")
      Button("Increment") {
        viewStore.send(.incrementTapped)
      }
    }
  }
}
```

**After:**

```swift theme={null}
struct FeatureView: View {
  @Bindable var store: StoreOf<Feature>
  
  var body: some View {
    Text("Count: \(store.count)")
    Button("Increment") {
      store.send(.incrementTapped)
    }
  }
}
```

## See Also

* [Reducer Macro](/api/reducer-macro) - Define features with the @Reducer macro
* [@Presents Macro](/api/presents) - Present child features
* [Observation Integration](/integration/observation) - Observation system details
