Search code examples
swiftuiviewbuilder

When is the correct time to evaluate a ViewBuilder in a SwiftUI view


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.


Solution

  • 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.