Given a custom content view that accepts a ViewBuilder as it's content, when is the best time to execute the ViewBuilder, in the custom views init or in its body?
struct Custom<Content: View>: View {
init(@ViewBuilder content: @escaping () -> Content) {
// Option 1
self.content = content()
// Option 2
self.content = content
}
var body: some View {
...
// Option 1
content
// Option 2
content()
...
}
}
This has been asked before, and the SO answer referenced this Q&A which provides an answer, but it seems somewhat unofficial. Is there any official documentation on this, or an explanation of the pros and cons of each approach.
It's better to pass a non-escaping function and evaluate early, if you can. So, in your example, option 1 and remove @escaping
. When a closure is passed non-escaping, it can be stored entirely on the stack, which is more efficient than storing it on the heap.
If you look at SwiftUI's built-in views, you'll find that many of them take non-escaping @ViewBuilder
arguments, which means those views are executing their arguments immediately.
For example, the content
argument of VStack.init
isn't @escaping
. This means VStack
cannot store content
in an instance variable. Therefore we can deduce that VStack.init
executes content
immediately, and cannot in the future be changed to store it for later execution (because adding @escaping
is not backward-compatible).
Compare with ForEach.init
, where the content
argument is @escaping
. This means ForEach
can store content
in an instance variable to call later, and we expect it to do so if, for example, ForEach
is used inside LazyVStack
.