-
Prior to updating my app to TCA 1.7, I had thought that it was perfectly reasonable to use light-weight computed properties in State – but is it really? BackgroundMy app mostly uses SwiftUI, but there are a handful of UIKit views (typically using UIViewControllerRepresentable). Per the TCA 1.7 migration documentation, I switched from using Combine publishers to observe state changes to the new TCA Questions
I have a feeling that I'm probably misunderstanding something here. I'd appreciate some additional insight into these issues. Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 6 replies
-
Hi @gerfarfel, we need more information to know what is going on in your situation. Ideally you would provide a minimal project that demonstrates some over-observing so that we can understand. But, here are a few remarks based on your description so far:
Lightweight computed properties are totally fine, even in 1.7.
No, the minimal amount of state is observed based on what state is accessed, regardless of computed properties or direct access to stored properties.
No, computed properties are fine to access in SwiftUI views.
This documentation is specifically about scoping of stores with computed properties. For example,
It's hard to say without a project to look at. Can you make a minimal project demonstrating the pattern you are currently following and then we can see what may be going wrong? |
Beta Was this translation helpful? Give feedback.
-
@gerfarfel In addition to @mbrandonw's post, I think the most important question is if you're actually observing a performance issue, or if you just noticed that a closure was being evaluated more often than before. Is your application still performing the same as far as the end user is concerned, or has migrating to 1.7's observation APIs introduced a noticeable regression in performance? As fussy as view stores and Combine publishers were, they did allow you to have absolute granularity in minimizing updates, even within a single view/controller, by deduping based on equality. But just because these updates could be scoped and minimized doesn't mean that the deduping didn't introduce its own performance issues. Swift observation, on the other hand takes a much lighter weight approach. Given a scope that accesses properties, it will reevaluate that scope if any of the properties are touched in a mutating scope, whether or not they change. Folks even asked on the forums if observation should further de-dupe based on equality, and the team replied that the de-duping operation was often more heavyweight than minimizing the re-computations themselves. So our changes align TCA observation more closely with vanilla observation, which we think is a good thing, and hopefully doesn't affect performance, but if it does, you have the same strategies you'd have in a vanilla observable app: you can take an observable scope (e.g. a SwiftUI view |
Beta Was this translation helpful? Give feedback.
-
Let me throw out a more detailed (but still simplified) example. I'm hoping to get some ideas on how to avoid scoping on computed state. Start with underlying data structures for an animation app. At the root level is an array of
In this example, the animation editor view is built up from several distinct UI elements, including the drawing canvas, a frame selector, a layer selector, a tool selector, settings panels, and more. That's a lot, I know, but if you're targeting a device with a larger screen (Macs, iPads), it's not untypical. The
In my current style of implementation, I would use computed state to generate the various view states, like so:
Now that TCA is using observation, the use of computed state causes small changes to be propagated out to far too many observers. For example, if the layer properties view enables the user to mark an individual layer as locked, that change would propagate back from Under pre-1.7 versions of TCA, the combination of cached stores and the use of Combine for UIKit observation meant that the implementation using computed state worked fine. Perhaps there were some inefficiencies issues under the hood, but I didn't see them, and the performance of the app was excellent. With TCA 1.7, the over-observation caused by the computed state is causing huge performance problems. Currently, I don't see an alternative to pushing the entire project state down to all of the individual view components (along with additional view parameters, like frame and layer ID's, yuck). Maybe there's another approach I'm not seeing, perhaps involving a complete redesign of the underlying data structures. I'll appreciate any direction @mbrandonw, @stephencelis, and the community can provide on this. Thanks! |
Beta Was this translation helpful? Give feedback.
Hi @gerfarfel, I think there is no good solution to this right now. There is just an insurmountable impedance mismatch between value types and sharing state. Value types aren't meant for being shared. They get copied when you pass them around. And sure computed properties got the job done in a pre-observation world (though it was a bit of a mess), but now that no longer flies with observation tools.
Luckily we do have a solution coming. We will be releasing details for how to handle shared state in TCA soon, and it's only possible thanks to observation tools in Swift. So I would say you may need to hold off on the observation tools for the time being and then take another look once we rel…