Skip to content

Changes to associated values in an @ObservableState enum does not trigger rerender #2899

@zgotsch

Description

@zgotsch

Description

When using an @ObservableState enum as my reducer's state, changing only the associated data of a swift enum variant does not cause views to rerender.

Example:

import ComposableArchitecture
import SwiftUI

@Reducer
struct TestNoNest2 {
  @ObservableState
  enum State {
    case string(String)
    case none
  }
  
  enum Action {
    case setString(String?)
  }

  var body: some Reducer<State, Action> {
    Reduce { state, action in
      switch action {
      case .setString(let newValue):
        if let s = newValue {
          state = .string(s)
        } else {
          state = .none
        }
        return .none
      }
    }
  }
}

struct TCAViewNoNest2: View {
  @State var store: StoreOf<TestNoNest2>
  
  var body: some View {
    Text(String(describing: store.state))
    Button("toggle") {
      if case .string(_) = store.state {
        store.send(.setString(nil))
      } else {
        store.send(.setString("zach"))
      }
    }
    Button("append") {
      if case let .string(s) = store.state {
        store.send(.setString(s + "zach"))
      }
    }
  }
}

#Preview {
  TCAViewNoNest2(store: Store(initialState: TestNoNest2.State.string("zach")) {
    TestNoNest2()._printChanges()
  })
}

When clicking the "append" button, I can see that the store state is updating, but the view does not re-render. When clicking the "toggle" button, which causes the case of the enum to change, the view is re-rendered as expected.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

I expect modifications to the associated data of my state enum to cause my view to re-render.

Actual behavior

Modifications to my state enum which do not change the case branch (and only change the associated data) do not cause the view to rerender.

Steps to reproduce

Click buttons in preview of the following SwiftUI code:

import ComposableArchitecture
import SwiftUI

@Reducer
struct TestNoNest2 {
  @ObservableState
  enum State {
    case string(String)
    case none
  }
  
  enum Action {
    case setString(String?)
  }

  var body: some Reducer<State, Action> {
    Reduce { state, action in
      switch action {
      case .setString(let newValue):
        if let s = newValue {
          state = .string(s)
        } else {
          state = .none
        }
        return .none
      }
    }
  }
}

struct TCAViewNoNest2: View {
  @State var store: StoreOf<TestNoNest2>
  
  var body: some View {
    Text(String(describing: store.state))
    Button("toggle") {
      if case .string(_) = store.state {
        store.send(.setString(nil))
      } else {
        store.send(.setString("zach"))
      }
    }
    Button("append") {
      if case let .string(s) = store.state {
        store.send(.setString(s + "zach"))
      }
    }
  }
}

#Preview {
  TCAViewNoNest2(store: Store(initialState: TestNoNest2.State.string("zach")) {
    TestNoNest2()._printChanges()
  })
}

The Composable Architecture version information

1.7.0, 1.9.1, 115fe5a

Destination operating system

iOS 17.2

Xcode version information

Version 15.2

Swift Compiler version information

swift-driver version: 1.87.3 Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5)
Target: arm64-apple-macosx14.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working due to a bug in the library.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions