How to Increase a SwiftUI View's Internal Reusability and Maintenance

The power of ViewBuilders

Pedro Alvarez
Better Programming

--

Photo by aldi sigun on Unsplash

One of the strongest points of SwiftUI is its power to make any View/component totally reusable in any possible context it's inserted due to its declarative way of defining and the ease to write a complex interface with just a few lines of code. Also, you can check in real-time the outcomes via previews.

But imagine the following scenario: you have multiple screens with kinda similar content and keeping them would increase the amount of duplicated code.

Wait, but in this case, transforming the common parts into reusable views isn't enough? Well, if the whole portion of common code was the same for both scenes, it would, but how about the case we may have some different pieces of UI right in the middle of each content?

I will illustrate the following scenario in order to allow your understanding:

We have three scenes above: the first corresponding to a simple personal info formulary, the second one for a hiring process and the last one for a health plan.

Three different contexts although the interface is kinda similar, except for the different headers and the availability header view presenting just for the health plan. Wouldn't that be practical if we could define from outside which context we should follow?

Identifying the differences between the contexts

In order to make that View reusable, we should first identify which are the different pieces of UI in that View that could be customizable.

As we said earlier, there are two places that may vary across different scenarios, which are the top header and the availability header, that can be present or not:

So, as we checked, we have 6 possible customizations for our View in this scenario: A personal info header view, a hiring information header view or a health plan header view for the top content and having or not the availability header view at the middle.

Building our view in different contexts

Now let's define how should our View struct be in a declarative way, let's first define our header views that may fill our screen:

Now let's define the root View:

This results on the health plan scenario with the availability header view

Repair that we simply placed the HealthPlanFormHeaderView and AvailabilityHeaderView as raw components, but what we really want is to turn them into customizable parameters. This is when we call the ViewBuilders!

The ViewBuilders are two parameters that are closures returning View types that we shall use to populate our root view:

Since, differently from the UIKit, we are dealing with structs and View is a protocol, we need to define two generic types which conform to the View protocol. We defined two different parameters to be injected to our RootView, respectively being closures returning Content1 and Content2 types.

When initializing our root view, we simply take the returned views from both closures and assign to the topContent and availabilityHeader properties. We also must tag those two parameters in the init method as ViewBuilders.

Now that we parametrized our top and availability header views, let's simply call it within the body:

Brilliant! Now that we parametrized those two components, we may pass any kind of View into this component that it will be displayed in its right place:

As a SwiftUI developer, you are actually familiar with this concept, since most native components to the framework actually receive ViewBuilders in order to customize its appearance.

Two great examples are the SwiftUI Button, which defines a behavior for when clicking it and then a label closure that describes its appearance. The same can be verified in VStacks and HStacks, which take some injected content and display it across an axis, being horizontal our vertical

These are views that are injected into another view as customizable parameters.

Spreading our Reusable View across different scenarios

Now that we have our RootView, that is a customizable component, we can reuse it inside other screens(Views) injecting the proper data needed for that context:

Respectively, we shall have these scenes:

If some new scenarios and possible headers raise, we just need to inject the corresponding instances to our reusable RootView, that is called in the screen's body.

ViewBuilders as private functions

As we saw, the ViewBuilder is a closure that returns a View type, but it may also be a named function inside your View type that establishes some parametrization for some piece of UI:

Now we created a piece of code that returns a customizable component depending on the hasHeader boolean.

If you have a too complex UI for your view and don't think creating separated files and Views to fill it is a good approach since these pieces of UI are made just for this context, separating different parts of your view into a ViewBuilder private method is always a good choice.

Summary

In this article, we described a way of improving a View's reusability, by turning some pieces of UI variations into customizable parameters. As your app is growing, you can always reuse a View by injecting some of its parts from outside.

ViewBuilders work as parameters to your View that are actually subviews. Any time you identify a screen that is kinda reused in different contexts, repair which parts are specific to each one and transform them into parameters.

I hope you enjoyed it and keep improving your code's beauty and reusability ;)

--

--

iOS | Android Developer - WWDC19 scholarship winner- Blockchain enthusiast