Handling ViewState transformations within the context of Observation #2722
Replies: 5 comments 17 replies
-
Hi @acosmicflamingo, you can still use struct ViewState {
let deleteSongsButtonHidden: Bool
init(_ state: Feature.State) {
self.deleteSongsButtonHidden = state.data.isEmpty
}
} And then when observing in the controller: class ViewController: UIViewController {
func viewDidLoad() {
observe {
let viewState = ViewState(store.state)
deleteSongsButton.isHidden = viewState.deleteSongsButtonHidden
}
}
} The act of initializing |
Beta Was this translation helpful? Give feedback.
-
It looks like what exists in the tic-tac-toe example is a very clean approach that gives me the |
Beta Was this translation helpful? Give feedback.
-
Hey there I have a question on the same exact topic. Advanced Strategies for State-to-ViewState Conversion in SwiftUI with TCA's New Observability Features I deeply appreciate the point-free team's work on the Swift Composable Architecture (TCA) 🫶, especially the new Observability features. These enhancements raise questions on handling complex state-to-ViewState conversions in SwiftUI projects, especially considering bindings and avoiding boilerplate. In our application, we face challenges with screens that have intricate states, represented by detailed viewStates mirroring the UI structure. This complexity is further amplified by direct 1-to-1 state-to-viewState mappings, which could potentially leverage TCA's bindings more effectively. For context, here’s a simplified model of our setup: struct BackendEntity { }
struct AppState: Equatable {
var loadingState: LoadingState = .loaded(backendEntity)
// Simplified for demonstration
}
struct ItemViewState: Equatable {
let title: String
let description: String
// Other properties for UI representation
}
struct ComplexViewState: Equatable {
let sections: [SectionViewState]
let isLoading: Bool
// More complex UI-related properties
}
// Example of a direct 1-to-1 mapping
extension ItemViewState {
init(item: Item) {
self.title = item.title
self.description = item.description
}
}
// Conversion from AppState to ComplexViewState, showcasing the complexity
extension ComplexViewState {
init(state: AppState) {
self.isLoading = (state.loadingState == .loading)
self.sections = sections(from: state.loadingState) // sometimes this is a very big conversion function
// Additional conversion logic
}
} With the latest enhancements in For context, we manage states with multiple nested sections and items, similar to an e-commerce product detail page, where the state includes loading indicators, ad information, and various section-specific data. In some of the latest videos I've seen examples, where did you do those kind of conversion inside the views themselves which are no go for our concrete scenario, due to it complexity. Thank you for your insights and suggestions. |
Beta Was this translation helpful? Give feedback.
-
Hey! TCA is amazing. Also, super stoked by the new 'SharedState'. I'm a fan of separation of concerns between UI and business logic. Ultimately, I would like to prevent the UI from having access to some child action reducers or state variables that aren't strictly related to presenting the view. Any way we can replicate the concept of 'ViewAction' that we initially had with 'WithViewStore'? This was a concept that I really liked. Thanks! |
Beta Was this translation helpful? Give feedback.
-
Oh, thank you for the answer and pointing me to the document. This is
great.
I definitely need to re-educate myself with the updated documentation.
Thanks again!
Sent via Superhuman ***@***.***>
…On Wed, Mar 20, 2024 at 1:08 PM, Brandon Williams ***@***.***> wrote:
Hi @Alex1478 <https://github.com/Alex1478>, this is already possible with
the @ViewAction
<https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/viewaction(for:)>
macro. You can also find information about it in the 1.7 migration guide
<https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.7#View-actions>
.
—
Reply to this email directly, view it on GitHub
<#2722 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACB6EOPOHXJ563YAWTSSXN3YZHUEVAVCNFSM6AAAAABCIASQYGVHI2DSMVQWIX3LMV43SRDJONRXK43TNFXW4Q3PNVWWK3TUHM4DQNJXGY3DE>
.
You are receiving this because you were mentioned.Message ID:
<pointfreeco/swift-composable-architecture/repo-discussions/2722/comments/8857662
@github.com>
|
Beta Was this translation helpful? Give feedback.
-
With Observation being around the corner,
ViewState
's time in the spotlight is coming to an end, and I wonder whether it's a good idea to start thinking about how to handle theViewState
transforming capabilities going forward. The value I saw from usingViewState
wasn't so much from it's ability to reduce the amount a store has to observe, but more from how it could translate a Store's state to one that has more meaning within a view's domain.To use concrete examples, I think it makes much more sense for a
MusicLibrary.State
to just comprise a property likedata: IdentifiedArray<Items>
whileViewState
would have a property likedeleteSongsButtonHidden: Bool
. Specifically, I always liked how I don't have to worry about anything view-specific in my reducer, but that I can just subscribe toviewStore.publisher.deleteSongsButtonHidden
that I can then assign todeleteSongsButton.isHidden
within my view controller. The transformation would come from here:That way, in my view controller I don't have to care about the
data
property and in my reducer, I don't have to care about thedeleteSongsButtonHidden
property.From what I've seen in the observation videos and PRs, the impression I have is that I would actually be doing something like this:
However, now I've lost the ability to test
deleteSongsButton
behavior (I have created my own tests that assert values from myViewState
). Of course, I could move this logic to my reducer, but it seems a bit silly that any time I am mutatingstate.data
, I need to remember that I need to update thedeleteSongsButtonHidden
property as well. A solution to this could be to use computed properties, but I believe that this will not work well at all with Store caching or the @ObservableState macro. But even if it were manageable, doesn't the reducer's state now become so much more bloated?What is the best way going forward in a post-Observation world that would allow me to keep my ability to test logic conducted within the view-domain, while minimizing adding view-specific bloat to my reducer's state?
Beta Was this translation helpful? Give feedback.
All reactions