Trying to get your SwiftUI code to compile

When I first saw SwiftUI, I instantly wondered how it was possible. It didn’t look like Swift. This foreign syntax was extremely expressive compared to UIKit. What used to take a ridiculous amount of UIKit code and possibly a storyboard, now takes ≤6 lines of code:

That’s an impressive 6 lines of code

SwiftUI’s declarative syntax is made possible by function builders (here’s the proposal). This is what allows you to combine and compose views together (e.g., HStack taking two View s and forming a TupleView ). Function builders are a huge step forward in Swift supporting declarative programming, and provide the basis for tons of powerful new DSLs.

I’ve been messing around with them lately, and I recently released a DSL for making HTTP requests. I learned a lot about function builders along the way. I hope the information I’ve included here can help you create your own DSLs.

Using a function builder

Let’s take a look at using the ViewBuilder that powers SwiftUI’s DSL:

Let’s break it down:

The @ViewBuilder function builder is applied to a function that converts two Text views into a TupleView that contains both Text views.

We don’t need a return statement because of SE-0255, which allows for implicit returns.

What the compiler does is convert our function into this:

As you can see, ViewBuilder does all the heavy lifting. It takes in our views and creates the TupleView. You can use your own function builders in the same way. Let’s look at an implementation of a function builder.

As of Swift 5.1 in Xcode 11 beta 4, we need to use @_functionBuilder instead of @functionBuilder . This will be changing once the full proposal is implemented and accepted.

What our function builder does is take in strings, and return those strings with “Hello” inserted at the beginning.

When we use our function builder, the buildBlock function is called, and it takes in any strings we pass. This is due to the variadic parameter ( String… , like how print accepts multiple arguments).

Inside the buildBlock function we can perform any logic we need to create our String array. In this case, we add “Hello” to the beginning.

Creating a function builder

There are endless possibilities when it comes to function builders we could make. I decided a good way to start would be something simple and useful: NSAttributedString .

In Swift, creating an attributed string can be a real pain. Wouldn’t it be nice if we could use SwiftUI style syntax?

We can create a function builder to do just that. Let’s start with our builder: NSAttributedStringBuilder

Here we define our function builder and the standard buildBlock . In this case, we take in multiple attributed string “segments”. Then we loop through them to create one combined string.

You can treat NSAttributedString... as an array

We want our function builder to work when initializing an NSAttributedString so we need to create a convenience initializer.

Now we’re ready to use our builder. However, to make the process even more seamless, I’ve created extensions for String and NSAttributedString to add modifiers. You can get the code for those here.

Ok, let’s try out our function builder:

That’s all it takes to create a function builder. However, you may notice this doesn’t work in certain instances. With SwiftUI you may see something like this:

However, if you try to do something similar with our function builder, you’ll run into this error: closure containing control flow statement cannot be used with function builder 'AttributedStringBuilder'

More build functions

To extend the functionality of your builder, you need to implement more static functions. The full proposal will support the following:

buildExpression(_ expression: Expression) -> Component buildBlock(_ components: Component…) -> Component buildFunction(_ components: Component…) -> Return buildDo(_ components: Component…) -> Component buildOptional(_ component: Component?) -> Component buildEither(first: Component) -> Component and buildEither(second: Component) -> Component

I will update this article as these become available. Currently, we can use buildBlock and buildOptional

buildOptional

As of Xcode 11 beta 4, buildOptional is called buildIf

Build optional allows optional values to be included in the block. This includes if statements, which upon being false will return nil

Here’s a simple implementation to handle the possible nil

Wrapping Up

So there you have it. Swift 5.1 function builders (in their current state). I can’t wait to see what DSLs pop up in the next few months. If you make something cool, add it to the list of awesome-function-builders. I’m excited to see what you all create!

You can view the finished code here.