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.
What is the architecture?
I used to use MVVM with RxSwift, which basically means controller is created by combining
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:
Structure of HomeViewBuilder:
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
Let’s also define
HomeViewBuilder that implements
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:
- Compose transformation from functions of type
- Apply this transformation to the created builder
Okay, looks nice, but how those
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
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
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.
Solution: let’s define another builder.
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
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:
greater. Those cases are equivalent of
NSLayoutRelationwith shorter names,
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
- 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
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! 😃