Result Builders

Create your own Swift-based, SwiftUI-like, descriptive domain specific languages

Sergio Ordine
Academy@EldoradoCPS

--

Photo by Glen Carrie on Unsplash

The first thing that draw my attention when studying SwiftUI is its descriptive nature to define views:

The view structure is somehow clear even to people that does not know deeply Swift or even programming: it is a view composed by a vertical stack that holds a label text and another stack, horizontal this time, that holds two other text labels.

So it generates a visual element that has a Text field "A Text" over "Second Text…" and "and another one…" fields side-by-side.

One thing that draws the attention is that body property definition as well as VStack and HStack subelements are just listed sequentially. This characteristics makes the visual hierarchy defined more readable and, thus, easy to understand.

But this is not a structure we are used in Swift. So the first question that arises is: is it a SwiftUI specific structure or can I use it somehow to create our own descriptive structures?

To answer this question we can analyze the SwiftUI View structure in order to get some hints:

The @ViewBuilder option that we see in both cases seems to do the job of creating this view composition in a descriptive way, almost creating a view construction language within Swift itself.

Those languages are called DSL (Domain Specific Language): a simple language tailored to work on a single, specific domain. In this case: a visual components hierarchy building.

DSLs could apply to several domains: a language to define rules from salary calculation, to define a web page structure, to define rules for billing a kind of service and so on. Those languages can make easier translate domain rules into code.

The ViewBuilder construction is preceded by an @ sign. Those constructions of Swift language are called property wrappers. Similar features are sometimes called annotations in other languages.

A property wrapper is, above all, a type. It encapsulates another type in other to add functionalities and processing to it.

Considering it is not the focus of this article, I’ll briefly give an example from this article, that also has further information about these types and how work of them.

The property wrapper @Capitalized wraps a String to make it capitalized. It can be used in a transparent way, so User’s firstName and lastName properties, whenever defined or changed, are always capitalized.

View Builder is also a property wrapper of an special type, called result builders (prior called funcion builder), that was created to support the definition of DSL-like languages within Swift.

So we got our first answer that leads to a second one: how to use it for my specific purposes?

A result builder is a property wrapper that can be applied to a functions, computed properties or function parameters that basically concatenates the result of each line of code that defines these and returns a single result. As an example, using the HStack we called before:

HStack initializer has a content parameter that is a closure with no parameters that returns a Content. This content is created by encapsulating the result of each line of code, in this case, two Text fields. Those text fields are concatenated as the content of this HStack. We could have components that can embody even other components (as another stack), creating a hierarchy.

We could do that without adding each constituent component as a subview, appending them into a data structure or adding any other control code. Basically we define what is contained in that stack instead of how we compose it. That is one of the main characteristics of this DSLs: being a declarative language.

The result builders are property wrappers, therefore a class or structure, annotated as such with the @resultBuilder property wrapper. Its required method is buildBlock, that has a variadic list of homogeneous parameters (a Swift function can have variable number of arguments defining them as Type…) and compose them into a single result value:

When a function, computed property or a funciton parameter is annotated as @AResultBuilder the function body expects that each line result in a element of Type. When all of them are executed, each one of them are used to create a single element of ResultType by the buildBlock function.

It answers, in theory, how to implement a structure like SwiftUI declarative view system. But some more practical examples could come in handy.

In the next part of this article, a practical example will be presented as well as the basic usage of result builders.

--

--

Sergio Ordine
Academy@EldoradoCPS

Software developer and educator. If you want to support me and my content production, please buy me a beer at https://www.buymeacoffee.com/sergioordine