Fernando Olivares

August 6, 2023

struct View vs @ViewBuilder

I've finally started learning SwiftUI by reading "Thinking in SwiftUI" from objc.io. I hadn't even gone a chapter when I realized I didn't understand what the difference was between a property that is a view, a property that is marked as @ViewBuilder and a function that is marked @ViewBuilder.

First, thanks to the book, I understood that if a single View is returned from @ViewBuilder, the ViewBuilder will do nothing and pass the single view back to whoever requested it. For example, the following View's body will be of type `Text`.

Screenshot 2023-08-05 at 22.35.06.png


If there's more than one view, the @ViewBuilder will build a TupleView, meaning it did flatten the interface into a single view. For example, the following View's body will be of type TupleView<Text, Image>

Screenshot 2023-08-05 at 22.42.20.png

So, what happens then if we return a property of type View to the body? Well, we can't.
Screenshot 2023-08-05 at 22.43.30.png


My disconnect was the fact that I didn't understand the difference between using SwiftUI's DSL and using Swift itself. Having learned that, I think I can explain the difference:

- A property that is marked only as View is just that: a view. It is a single view. We could return a view that itself will combine its subviews into a single view (like an HStack) but the property cannot use SwiftUI's DSL.
- A property that is marked as @ViewBuilder is allowed to use SwiftUI's DSL in order to reduce the contents of the closure into a single view. This is why View's property body can accept both the Text and an Image and will return a flattened view.
- A function that is marked @ViewBuilder is also allowed to use SwiftUI's DSL, will also flatten its views into a single views, but can accept parameters. A function marked as @ViewBuilder and accepts a String as a parameter, would be the equivalent of a new struct View that has a String property.
- A @ViewBuilder function will only flatten the elements; it does not decide on the layout of these elements. This is the difference between returning two Views in a body (which will return a TupleView<ViewA, ViewB>) and an HStack with two views in it (which will return an HStack<TupleView<ViewA, ViewB>>.

From this, I gather that separating your Views into smaller views could be done in two ways:
 
- Creating a new struct that conforms to View
- Creating a new function that is @ViewBuilder

I think the difference between these two is that the former is much more reusable than the latter. Basically, if I think the build subview should be used elsewhere in the app, then I should create a new struct for it. If I think this subview is strictly for use inside the struct where the function is declared, then the latter makes more sense.

I hope that makes sense!