Tuesday, August 16, 2022

Slow SwiftUI Closure Actions

Luke Redpath:

TIL - if you’re creating your own SwiftUI environment keys where the value is some kind of “action”, modelled as a closure (e.g. () -> Void) you might see unexpected re-renderings of views that use that @Environment property.


My hunch - which turned out to be correct - was that closures in Swift are reference types and SwiftUI gets confused when its environment holds reference types. As a clue - note that similar built-in values in SwiftUI are actually structs with callAsFunction() methods.

Update (2022-09-09): Rens Breur:

The underlying reason for this bug is another cool SwiftUI optimization: If a @State variable is not used in the evaluation of a body, changing this variable does not trigger a view evaluation, and does not trigger an update to the view's properties to there values as managed inside the SwiftUI framework. It is a smart optimization, and works well together with memoization: if an input variable is not used, the view does not need to be re-evaluated when it changes, and when it is used, the view only needs to be re-ealuated when it changed.

But in this specific case, it does not work correctly. When we evaluate the body for the first time and the text is collapsed, the state variable is never used, so it seems that it is not needed when evaluating the body. But when we expand the text, the state is read all of a sudden, even though the ContentView itself is not re-evaluated, and the @State is not properly prepared.

There are ways to fix this bug while keeping our content as a closure, but we are now fighting with SwiftUI optimizations instead of making use of them.


Comments RSS · Twitter

Leave a Comment