Alert presentation from a Binding

Hey all,

I've got a problem where setting a state property from nil to a value isn't correctly presenting an alert, when using TCA, and it's leading me to wonder if there's a problem with my work/understanding, or if there's a bug in TCA?

We're using a mixture of MVVM and TCA, for state management. As such, the ViewModel is retaining a reference to the ViewStore, with the View interacting with the ViewModel through Bindings derived from the ViewStore and methods to send actions.

I've created a Binding to the alert property on my AppState, and I can see that the state changes to fill that alert property when I click the "Show Alert" button. But the alert itself isn't presented. In debugging this, I can see that if I use a Store directly, and use WithViewStore, the alert presents correctly.

I've looked at using AlertState, but that pushes too much of the alert configuration into the State for how I'd like to use it - I'd really prefer to just use the identifiable Binding.

My example code is in the repo below. Any help would be greatly appreciated!

Hi @rhysmorgan, although you are holding a ViewStore inside your ObservableObject view model, it doesn't seem that you have any @Published properties in the view model. That means it will never communicate to the view that state changed so that the view can re-render / present the alert. So, even though the alert field flips to a non-nil value the view will not be notified of that change.

Is there a reason to wrap the ViewStore in a view model? Why not use the ViewStore directly in the view?

Hi @mbrandonw!

Thanks for replying so quickly! I mostly see now why it's not updating, with the lack of @Published properties. Adding an @Published property that I set after the AppState changes forces the alert to present. I guess I assumed that when a Binding value changes, the View would be notified, but that doesn't seem to be happening for my Binding<AppState.Alert?> - not until the ViewModel is forcibly refreshed.

One of the main reasons is that I'd like the view to just get bindings and call methods on the view model to actually communicate with the store, being unaware that the state management/dependencies are managed by TCA. Perhaps I'm misunderstanding how to mix MVVM and TCA though, and the View should always retain a reference to the Store, even when using a ViewModel?

This may be going off topic, but why are you trying to mix in MVVM? What benefit do you think wrapping a ViewStore in some kind of view model gains you? It’s barely more than a facade.

I find it helps to think of the ViewStore as your view model. It serves the same purpose. It exposes a scoped read only projection of your state and provides a means to trigger actions through view interactions. It already provides conveniences to create bindings.

Using the view store directly in your view couples your view to TCA, but your entire app architecture is coupled to TCA so I don’t think you’re gaining anything by trying to hide it behind further layers of abstraction.

It’s possible to add new helper methods to a ViewStore that are specific to your view using an extension constrained to the appropriate state type, if that helps with readability and keeping the view code succinct.

I'm keeping them separate to keep my view presentation logic separate from my state management logic.
For example, with a UITableView, my State contains the raw model types in an array. My view model subscribes to the Publisher for that property, and transforms them into a ViewModel type for each row. I can write separate tests for the Model -> ViewModel transformation.

...unless I should be storing these ViewModel types directly in my State, rather than the Model types they're derived from?

Your state should already describe the data your view needs. You shouldn’t need to do any further transformation unless it’s purely presentational.

If your table view cells expect your model objects to be wrapped in some kind of presenter (which is what it sounds like to me) then I’d just wrap them at the point I pass them to the cell.

If this wrapper is more than just a presenter - your cells also need to send actions and have substate of their own, then you can have state and actions for each item and produce a view store for each cell scoped to that cells state.

It’s hard to provide a more concrete example without knowing exactly what your view model is doing but I’m fairly confident you should be using either a view store, or a presenter that just reformats your model data.