Did you try to use something like TCA? You can scope the stuff you need to exactly only re-render what you need. Otherwise re-render the world on ObservableObject etc. Observable only alleviates the scoping-part a little and does not help with re-render the whole body.
Some things still re-render the world and are VERY slow though (ifs/switch inside ForEach with many rows easily brings app to hard lagging, even if doing everything to prevent it -> makes SwiftUI useless for generic row usecases).
Could you add the date of publication to the top of the page on each blog post? (Or year+month.) Looks like they’re there in the blog list, but not on the individual posts?
Would help understand the context on posts that describe tech that has evolved rapidly and probably still will
It’s kinda like React in that way. And it’s similar in that newer devices run it well enough that devs might think they don’t have performance issues at all, but when using an older iPhone you can start to see it stutter.
The article matches my experience with SwiftUI [1][2]. For example, AFAICT, it's not really possible to write a usable node-graph editor using SwiftUI due to layout and dependency analysis overhead. You have to put the entire node graph inside a Canvas and do your own event handling, which is what we did in [3].
UIKit and AppKit aren't slow though, and Apple has every incentive to make this faster (they wrote SwiftUI's dependency graph in C++ after all, so they do seem to care), which makes me think there is just inherent slowness from the dependency analysis that is required for these sorts of systems (that seems to be what I see on the profiles).
There used to be a behavior in UIKit where you could scroll a view and tap to stop the view and quickly tap a button to move on to another screen, and at some point in time when Apple moved away from skeuomorphism, the UIView scroll behavior changed in a way that from then on forced users to wait for scrolling to stop before tapping a button, but they also injected a full fraction of a second, probably somewhere between 200 to 600ms before you could interact with UI controls again.
iOS has felt so stupidly slow since then, because you're waiting for all of these dumb screens to swipe and pan and you can sit there and tap a button in the middle of all of this like 3-5 or more times and the damn phone does nothing until everything is all settled and complete, and only then will it listen to your tap event.
Who are these utter grandpas who make these stupid UI decisions?
> There used to be a behavior in UIKit where you could scroll a view and tap to stop the view and quickly tap a button to move on to another screen, and at some point in time when Apple moved away from skeuomorphism, the UIView scroll behavior changed in a way that from then on forced users to wait for scrolling to stop before tapping a button, but they also injected a full fraction of a second, probably somewhere between 200 to 600ms before you could interact with UI controls again.
Is this specific to certain types of scroll views? I just played around in my Mail app inbox (most likely built with UICollectionView) initiating flick-scrolls and then double-tapping to stop the scroll and select the message under the tap and it works exactly like the original behavior you describe with zero delay.
If there’s one thing I could ask for when it comes to SwiftUI it would be for them to stop stripping symbols for the framework. It’s a complete black box in the profiler unless you put an unreasonable amount of effort into reverse engineering it.
I’m an ex gamedev with the misfortune of having to work with a legacy SwiftUI project. SwiftUI, and Jetpack Compose on Android, are basically react-style functional dom-diffing style renderers and have all the problems that come from that. What’s worse is that often the project has to be recompiled in order to preview changes with this functions-as-UI-components philosophy
The dom-diffing style makes me think of one of my beliefs about software. You have two styles.
An active style where where when state is updated side effects happen directly inside the same call graph.
And this passive disconnected spooky action at a distance where you update state and something else is supposed to/might execute side effects at some unspecified time in the future.
The first way there is no magic going on. The second way it's all magic. I try to avoid writing code the second way if at all possible.
The first style is the source of uncountable UI bugs. I have lost track of the number of times I’ve seen the developer forget to set a dirty bit to redraw the screen when some state changes. It’s the imperative vs. declarative model. Declarative may often be slower but it has other strengths.
Don't get me wrong. I totally understand that your way is why we don't see annoying rendering bugs anymore. We just put up with UI's that are slower than 25 year old UI's on machines that are a 100 times faster.
At least with web apps, that tends to be the “Redux” style more than the “React” style causing that. Basically, lazy state management (and I mean this pathologically rather than in terms of Haskell’s ‘lazy’). The laziness comes from the business side of things, squeezing more productivity out of engineers per dollar. The declarative style doesn’t slow down web apps very much, it’s rerendering every component any time a state changes anywhere that does.
On mobile it’s a trade off of rendering bugs with UI bugs. I’ve seen issues on older Android phones where Compose causes the text field to lose focus and flicker when typing if autocomplete is switched on. The workaround is to tell users to switch it off in the OS settings. And of course the latency issues are UX concerns all their own
It’s important to mention that SwiftUI is for some reason in a terrible state under macOS. You notice that mostly when you try cross platform apps such as IceCubes. iOS version works great but the Mac version has a terrible performance.
I find Xilem and the other work that Linus is doing very interesting, but at this point I've lost hope. The momentum just isn't there, maybe the overlords of Google can be convinced to put more resources behind it because if I extrapolate where this is going from where they are at now it is not going anywhere.
SwiftUI apps get basic things about the experience wrong. Things like margins around window borders and interaction with system UI.
SwiftUI apps stick out like a sore thumb, just as bad as Electron if not worse. The worst offenders on my phone are SwiftUI. And it is horrible on Mac - see System Settings, which has somehow managed to turn their lead over Windows into being worse than the Windows 10 era settings app.
SwiftUI apps are. not. native. Full stop.
Apple needs to try harder if they want to maintain their reputation for high-quality, high-polish, premium software experiences.
SwiftUI is a black box, yes, but under the hood, it's just wrapping all of the same AppKit/UIKit controls.
It likely started as an answer to React for iOS and then late became a cross-platform toolkit. The API to get what you want and expect is terribly documented and poorly named. Countless times I stumble across the "right way" to do something, and it works well.
You just won't find it on StackOverflow (or ChatGPT) nor in any tutorial site. It will be buried in a small section of sample code from Apple or listed on a slide in a WWDC video.
My biggest complaint is that it tries to be too magical. Sometimes certain values are ignored and no amount of working around it will force it. Some internal decision for optimization didn't consider a specific use-case and there's no way to know. All I get is, configured one way, everything works, but change it, and suddenly it's ignored.
It took UIKit a decade to ease up on magical behavior, but SwiftUI didn't seem to learn that lesson and so it's back to square one.
> These damn animations man... they're so fluid and springy and easy to use in SwiftUI, that it lures you into adding them everywhere.
The constant animations are what makes me dread and despise working with Apple software. Each time something moves for no good reason whatsoever, I die inside a little. Every few seconds.
> [...] the same animation was driving me nuts because I was switching spaces so often, that the animation was slowing me down
Exactly. Props to the author for making them possible to turn off. The operating system at large still has _tons_ of them that you can't get rid of, but it's the thought that counts.
> Reacting to keyboard events is still something I do outside of SwiftUI
SwiftUI is a nice idea in principle, what with being reactive, but I found that I cannot achieve anything worthwhile without piercing down into the lower layer of AppKit. And then I asked myself why do this clownery in the first place.
Ultimately, trying to learn Apple frameworks has made me love GTK+, because it's so beautiful and elegant in comparison (disregarding that it's currently deeply broken on macOS).
> SwiftUI is a nice idea in principle, what with being reactive
I want to like reactive, but between its inherent properties, how SwiftUI does it, and the fact I'm having to use VIPER as the architecture for the project…
…I don't like it, but I also don't know exactly where the issue is. And "not knowing where the real problem is" means I also can't add any productive suggestions for how to improve matters, to the very obvious annoyance of the iOS lead.
> I'm having to use VIPER as the architecture for the project…
This is your problem. VIPER is just a huge amount of worthless bureaucratic toil. It’s only useful if your only option to scale development is putting as many warm bodies in seats as possible and need to enforce mediocrity to keep things from going off the rails. It’s not great in general and particularly bad for SwiftUI.
This 100x. A VIPER codebase I inherited for some client work a few years ago was perhaps the most cumbersome and tedious codebase I’ve ever had to work with. Needless to say they missed their launch window and market opportunity.
This is one thing I like about Android. In developer settings you can make every UI animation twice as fast or even turn them off entirely. It's the first thing I do on every device I use.
I think animations are fine and even good as long as they're communicating something to the user (not just done for flare) and perhaps more critically, are non-blocking (e.g. the UI is still functional during the animation) which makes the ability to toggle off animations less necessary.
Switching spaces on macOS is one of the best examples of easy improvements. There's info being communicated (where the user is being swept away to, where the destination desktop sits in relation to the current one) but it should probably be sped up by at least 40% and not block user interaction so if there's a focused textfield at the destination desktop, the user can start typing mid-transition and no text will be lost.
Yeah, UI animations can be good. The vast majority are bad, and even the good ones are almost always too long. That's why it's nice to have a setting for them. Those macOS animations you mentioned have no settings to make them faster and they are infuriating.
On phones I usually speed animations up rather than turning them off entirely. But on the Pixel Watch the animations are so gratuitous and add so much friction to my use of the watch that I turn them off entirely.
I haven't tried a Pixel Watch, but on phones I find Android animations generally kind of awkward. They too often feel like they don't fully connect with what they're animating, if that makes sense, and the curves they're on are often odd. I could definitely see the appeal in turning them off.
What are your favorite parts of gtk+? I'm working on my own UI framework and trying to borrow myfavorite parts of the lot of them. Gtk+4 seems to have some decent stuff, but I've only dabbled with it and worked more with old school QT. I always liked the signals a / slots myself.
Mine was going to be a lot more aggressive in favor of Flutter, ex. SwiftUI doesn't have hot reload, looks like it _always_ needs to repaint on a state change (whereas Flutter has that widget | paint boundary that keeps things brutally efficient). Once you use InheritedWidgets / Provider / Riverpod you get provably minimal CPU usage, i.e. minimal rebuilds, the subwidgets never repaint unless you want them to. Top it off with pervasive StatelessWidgets and you have provably minimal RAM usage.
I guess what I'd say is, it doesn't add much to the conversation: every view framework that needs to repaint a deep view tree entirely has issues, and in retrospect, there's deeper observations to make w/r/t SwiftUI and flutter.
Flutter has had its performance issues in the past (like scrolling on iOS, and shader performance issues), and I recall someone saying that Flutter apps lag on an Android phone that is still able to play the latest games decently.
However, things are looking up with the change from Skia to Impeller and I'm hopeful about the future.
Way, way, way overrated, the "write a brand new rendering engine to eliminate all runtime shader creation" leaves me much more confident speaking strongly in public.
Flutter in 2020 could render a complicated UI (new Google Assistant UI with glowing effect) on a 2015 $200 Android, and there was simply no way to do the same in Android proper. (Source: built Android version at Google and built Flutter version after for no particular reason, side project to teach myself Flutter)
People tend to remember Flutter anecdotes that correlate well with previous xplatform framework fundamental issues, but in Flutter's case, they're not fundamental, they're more "holding it wrong" / "could use better docs"
That being said it's probably a year or two away from me being brave enough to post that on an HN article (safe here, because articles old, weekend, and not directly related to Flutter).
Nice to see we're mostly in agreement, since (as I said in my previous comment) I'm hopeful for Flutter's future. The team writing their own rendering engine is unexpected and I hope to see the same engine used in other projects too.
https://news.ycombinator.com/item?id=33772876 (189 comments)