Functional View Building

Creating views in Storyboard or in the code? As iOS developers we know that question very well. Both of them have their pros and cons but recently I more and more prefer code-created views.

Szymon Mrozek
Mar 20, 2018 · 3 min read

I used to use MVVM with RxSwift, which basically means controller is created by combining Storyboard , ViewController and ViewModel. If we remove Storyboard interface where is the proper place for building the view?

Let me introduce ViewBuilders.

Consider a situation when we’re building some controller called HomeViewController. Let’s create a helper structure named HomeViewBuilder which finally returns HomeView, an abstraction that allows us to access components (equivalent of outlets).

So we end up with the following architecture:

Maybe you’ve already guessed that we want to build this view by piping some operations like adding / setting proper components. Let’s define a simple ViewBuilder protocol:

Let’s also define HomeViewBuilder that implements ViewBuilder.

The most important part is of course buildView function. It’s intentional that I’ve shown you now the final version of this method. It is the only public method that is accessible from the outside and it’s a kind of a dream, which we want to fulfill now.

First, let’s introduce popular piping and function composition operators that allow us to combine functions and objects:

Pipe operator is used in a buildView function and it basically looks like:

  1. Compose transformation from functions of type Builder to Builder
  2. Apply this transformation to the created builder

Okay, looks nice, but how those install / setup functions work?

If you don’t know much about lenses you should definitely watch some videos how this pattern works. I’ve used them here to write separate, small pieces of code and make them reusable. Lets consider creating UIStackView with vertical axis and translatesAutoresizingMaskIntoConstraints flag set to false. How many of them we’re going to use in the project? Of course a lot.

I’ve created some project-global Style structure that defines commonly used styles, which are the transforming functions of type (View) -> View. Here are some examples:

So let’s build UIStackView that uses one of those styles. Here is an implementation of typical installer function:

Thanks to lenses composing it looks quite clear and we’ve just gained a lot of reusability in our app. But wait, we’ve missed important constraints setup …

By using anchors we can quite easy generate constraints by connecting one anchor to another. But here we loose the ability of doing it in a flow and generic similar to the one that is used with lenses.

I’ve created a library called FunctionalBuilders which wraps constraint building and makes its using much nicer. Its construction is similar to the ViewBuilder . Here are the missing lines from installLabelsStackView function:

It again looks like flow-composed set of functions, we once set the destination view (the one that we’re attaching constraints) and pass only source for the constraints generating functions. There are more typical options except constant available to pass for constraint generating functions like:

  • relation enum of equal, less or greater. Those cases are equivalent of NSLayoutRelation with shorter names,
  • priority
  • multiplier (for width and height)

Feel free to use that library (but please note that it is the very beginning of its development stage 😅).

So here I present final HomeViewBuilder code with installing all of the components and attaching them to the HomeView:

Final notes

  1. How to use it after all?

That’s all from the HomeViewController. Looks perfect!

2. When writing ViewBuilder define short methods that perform some specific logic, exactly like install, setup methods.

3. Define one Style struct with common styles and make fileprivate extensions for creating reusable styles in the same builder struct.

4. Of course it is possible and recommended to use some other view builder inside view builder.

5. I like to define typealias Builder = ... which makes definitions shorter inside particular builder.

Feel free to suggest some better solutions, as I said earlier it’s a very beginning of FunctionalBuilders development.

Thanks for reading, and feel free to leave claps if you enjoy it! 😃

AppUnite collection

We teach how to write apps