SwiftUI tips and tricks

Optional ViewBuilder closures in SwiftUI

How to create a custom view that accepts optional content through ViewBuilder closures

Matteo Puccinelli

--

ViewBuilder closures are one of the most important and widely used function builders in SwiftUI. They let you create a view out of a closure containing multiple child views.

For example, we might create a view called RedPage which is a reusable full screen view with a red background, a main content and a header:

ViewBuilder closure.

You can use it like this:

ViewBuilder closure usage.

The result is:

ViewBuilder closure example.

Let’s make the ViewBuilder optional

Now, what if I wanted the header in the red page to be optional? I want my custom RedPage to be called like in the example above, or just like this:

RedPage with nil header.

If you try the last snippet the compiler will complain that:

Generic parameter ‘Header’ could not be inferred

The compiler is right (it’s always right 😤). And I bet you’d like me to try to change the RedPage init into the following:

Nice try. It would have worked for a standard closure, but ViewBuilder closures are not real closures. They are function builders and the things work differently here. The compiler would say:

Function builder attribute ‘ViewBuilder’ can only be applied to a parameter of function type

No worries, we can fix this issue by leveraging the Swift where clause and the SwiftUI EmptyView view. The where clause in Swift can be used for several things. In this case we’ll use it to create an extension of RedPage that specifies a constraint on the header type.

RedPage extension.

EmptyView is a special kind of view in SwiftUI that fits perfect for this situation. Apple says:

[…] EmptyView represents the absence of a view. SwiftUI uses EmptyView in situations where a SwiftUI view type defines one or more child views with generic parameters, and allows the child views to be absent. […]

Now everything works as expected and the result is:

Optional ViewBuilder closure example.

Just another little tip

If you need your custom view to accept more than one optional content (for example a version of RedPage with an optional header and an optional footer) you’ll probably end up writing:

RedPage multiple extensions.

Fortunately, Swift 5.3 allows us to write the snippet above in a more concise and meaningful way. Apple introduced the ability to attach a where clause to functions inside generic types and extensions. Thus, you can turn the code above into:

Swift 5.3 where feature.

--

--

Matteo Puccinelli

Passionate iOS developer. SwiftUI/Combine early adopter. Author of SwiftUI NavigationStack https://bit.ly/2H6YjQS