The Best of Everything

Or how to get the best of Interface Builder in code

There are two main ways of building native user interfaces for iOS — visual (using Xcode Interface Builder) and programmatic (with code). In my post, I’d like to turn the spotlight to the problem of two concerns (construction and usage) being mixed¹ out-of-the-box in the process of creating user interfaces with code and highlight my — inspired by the Interface Builder — way of dealing with it.

First, let’s take a look at both approaches in practice. Let’s consider some very basic UI:

The UI consists of four things:

  • Non-interactive UILabel with a placeholder text
  • Interactive UITextField that we want to make accessible to the view controller (to be able to read it’s text property)
  • Interactive UIButton that we don’t have to manage at the view controller level, but we want to handle a tap action related to it
  • Non-interactive UIStackView that keeps all of the above together in a vertical layout, also without a necessity to be directly managed by a view controller

The functionality is dead-simple — whenever a user taps button we take a text from the text field and print it to the Console.

The implementation of related ViewControllers looks like this:

User interface created in Interface Builder vs Code

There is a significant difference in the volume of code that you have to write up there and the best code is no code at all², but that’s not the main issue. There are valid reasons to write user interfaces in code³ (reusability, ability to develop in parallel) and accept it’s intrinsic downsides.

What I really appreciate about the implementation on the left side is the clarity. It is super clean and focused solely on the usage while leaving the construction responsibility to the default implementation of the loadView().

Given that the separation mentioned above is not available out-of-the-box when implementing user interfaces in code, is there a practical way to avoid this outcome? The answer is yes, even more — Good Programming Is Like Good Writing and there is no single “right” way to write any implementation and there is a number of possible solutions:

“The other part of it is also having a grand or free mode of expression that there are many different ways that you can say something” — David Heinemeier Hansson

Mimicking the Interface Builder in Code

To mimic the Interface Builder in the code I tend to focus on keeping all the construction logic in a separate file (as it goes with .storyboard or a .xib) and expose to the view controller only what matters. Also, I prefer to keep that logic in a nested class to not clutter the namespace of the view controller with build* methods.

There is a couple of rules out there:

  • non-interactive, placeholder views and controls (e.g our somethingLabel, stackView) are built using functions with private access level and are entirely not exposed to the view controller
  • interactive views that we want to manage in a view controller, as well as instances of the views offering actions that have to be handled by a view controller, have to be built outside of the main buildView method and injected into it

The comparison of view controller implementations looks a bit better now:

User interface created in Interface Builder vs Code + Builder

As I‘ve written before — there is no golden rule. That’s how I do it and if you like it feel free to adapt it to your project. What matters the most is consistency and my approach affords it across multiple cases.

When it comes to the granularity of our nested Builder class we may want to integrate buildView with buildRootStackView and buildSomethingLabel methods depending on the complexity of our views (creating a subclass of the UIView is another option).

The static nature of the Builder may also be not sufficient for all cases — if your view is more dynamic or customizable you can consider leveraging dependency injection here and leverage non-static methods.

This approach lets us keep the minimal footprint of the UI construction code in the view controller. Having a view controller that is focused on the usage instead of both usage and construction makes us more certain that we can avoid a Massive View Controller problem. In most cases, it allows you to remove tens or hundreds of lines of code from it.

You can find the related source code here.


I’m Maciek Czarnik

iOS Developer, musician, maker. Passionate about building beautiful, robust, useful and user-friendly apps. Contact maciek.czarnik@gmail.com for project inquiries.

www.mczarnik.com