Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It comes down to understanding the reactivity model - how to define state that can change, and how efficiently the UI updates in response to those state changes.

I think the issue the author had with SwiftUI is not understanding the reactivity system, or frequently choosing an expedient option that caused over-rendering - unnecessary re-renders in response to state changes that aren't required for the UX to work correctly from the user's perspective. Even if your UI's actual rendering work takes 1ms to paint, you want to avoid re-calculating busy work, allocation, etc, which can drive poor experience.

With a framework like React that provides very few primitives, it's quite clear when the framework will re-render. With a complex framework like SwiftUI, there are many primitives and different ways to represent state, subscribe to updates, etc, and the framework is quite under-documented. After spending 15 hours with SwiftUI, I still don't have a clear understanding of how the system detects a subscription, or what the re-render semantics are.

React I would describe this way:

1. Every re-render starts when you setState to a value that does not (!==) equal the current value in a parent component, or a reducer stores a new value internally.

2. Child components of that component will re-render except in two cases:

2a. The child component uses React.memo, and the props passed by the parent are shallow-equal to the previous props

2b. The child component JSX nodes are strict-equal to those returned by the previous render of the parent component, eg a subtree in the parent wrapped in useMemo

I can't come up with a similar definition for SwiftUI after 15 hours building an app in it. I need to consider @Binding, @State, ObservableObject, @ObservedObject, projected values. When is the system creating fine-grained subscriptions automatically? When is the system subscribing to an entire object (deeply?). Hard to find a cogent explanation of all the options.

Because of my different understanding of these frameworks, I personally write React apps that perform better than most people expect web apps to perform, and SwiftUI apps that perform much worse than people expect Apple native apps to perform.



The talk "Demystifying SwiftUI" provides a great deal of insight into how SwiftUI performs diffs.

Basically, unless an identity is explicitly specified using `.id()`, it uses the static types of your views to determine their identity. Naturally, this has a huge effect on what is re-drawn, but also animations.

This is also why AnyView can lead to worse performance - it erases the static type information.

[1]: https://developer.apple.com/videos/play/wwdc2021/10022/


Thanks for the reply. One thing I love about Flutter is that it's doing tremendous job under the hood to decide what parts of the widget tree is actually need rerendering. I might be wrong, but that's probably the major aspect of their widget tree system design. As a developer I focus mostly on the expressing of the UI via widget tree, and if I follow official examples and tutorials and do not invent the wheel, than it yield optimal performance.

If I understood your comment correctly, in SwiftUI developer should learn a lot of stuff to make re-rendering upon state change efficient?


> When is the system creating fine-grained subscriptions automatically?

With TCA you can side-step these issues to an extent, in that you can scope the store down to what the view needs. Then, the view body only re-renders when the data you scoped down to changes.


What is TCA?

I'm frustrated also because there's often times when we want to pass state from a parent view through an intermediate view to a grandchild view. How can we avoid excessive re-rendering in the intermediate view? Often I feel like I'm able to scope down subscriptions, but not eliminate them entirely, again probably because I don't understand the system well enough / the system documentation is lacking.


TCA stands for Composable Architecture. I don't understand the system well enough either, feeling like everything re-renders. That's why I just use TCA instead. The library also needs discipline in what data is made available locally and in how much changes the system can take before things get laggy due to architectural constraints. But it can show where and why TCA view bodies get re-rendered.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: