That One Closure That Made SwiftUI Slow
Why closures can trigger unnecessary SwiftUI re-renders and how to avoid it.
SwiftUI and generally declarative UI allows us to ship screens faster than ever. But writing UI code quickly doesn’t automatically mean our app performs well.
One of the most common performance issues in SwiftUI is excessive view re-rendering, meaning that body property get recalculated more often than needed. Well what’s the problem? This can lead to FPS decrease and make app scroll not smooth 🐢.
👉After this article you will know why some people believe that closures are evil to SwiftUI and we will take a look at some suggestion both by community and Apple itself.
When does SwiftUI re-render a view?
To understand the problem, we need to clarify when SwiftUI actually re-renders a view. A view is re-rendered only when one of its dependencies changes.
By dependencies we mean the properties the view’s body depends on.If none of those properties change, SwiftUI can skip that view during diffing and reuse the previous View ⚡️.
ℹ️ This is why it is considered a good approach to split your View into smaller ones. So that SwiftUI diffing can skip views that need no re-rendering.The problem 🧐
You have:
a parent view
a ChildView
a closure passed from the parent to the child
a button that triggers a redraw
What do you think should happen when you tap the “Redraw” button?
Should ChildView be re-rendered? Take your time ⏱️
Let’s use Self._PrintChanges() and tap the button 8 times and see what happens.
ChildView is re-rendered every single time, even though none of its visible data changed. 😱
❗️Imagine the cost of something like that on a DashboardScreen that has many components inside.Why is this happening?
The root cause of the problem is that swift closures and equitability doesn’t go well together.
Stay to the end to see what Apple engineers recommended in their recent Q&A session.
At this point you might be thinking “How many closures have I passed to my views?“ Well you are not alone 😅
Solution:
One of the quickest solutions is to wrap the closure and provide your own Equitability. This will make SwiftUI diffing life so much easier but the developer will have to explicitly change the properties that define the equitability (the id in this case) so that the childView takes the updated closure.

So… are closures the real problem?
Short answer: not really 🤔
This isn’t something Apple can “just fix”. The real issue is what closures capture. In our example, the closure implicitly captured self because it depended on foo property .That means the closure effectively depends on the entire view value, not just one property and that explains probable the reasoning behind closures and equitability.
For the shake of this article I did some experiments and explicitly added foo in the capture list and the body was not recalculated every time ⭐️What Apple engineers said?
Q: Is there a good pattern to pass an action closure to a view while minimizing impact, given that closures are hard to compare? Is there a more performant alternative?
A: Try to capture as little as possible in closures—for example, by not relying on implicit captures (which usually capture self and therefore depend on the whole view value) and instead capturing only the properties of the view value that you actually need in the closure.
Big thanks to Anton Gubarenko for writing the Q&A from Optimize Your App’s Speed and Efficiency session.
See you in the next article, till then I wish you have many productive coding sessions ⭐️
Konstantinos Nikoloutsos




