Programmatic Views: Consistently better user interfaces

Dan Sinclair
poq-techblog
Published in
4 min readFeb 16, 2021

What are programmatic views?

A programmatic view, or rather a view created programmatically, is a view that is not created using a .xib or a .storyboard file. Its user interface (UI) elements, layout, and style are created entirely in code. “Great! But, why?” I hear you ask. Well, let me explain.

Why create views programmatically?

We’ve noticed that when working with a larger team (or if you’re working solo and just unlucky) resolving merge conflicts in .nib and .storyboard files is a nightmare. I’m talking about a Freddy Krueger kind of nightmare, and we want to reduce the nightmares our iOS Developers have at Poq, so we opted to find a new way of creating views in iOS apps.

Another big reason for us to go programmatic was to be able to customise the layout without having to copy-paste an entire .nib file from one internal module to another and then modify it. This was clunky and tended to cause hard-to-find crashes when the views the .nib referred to were in an inaccessible/wrong module.

Using programmatic views means that we are able to modify constraints with precision where required.

Our goal was to eliminate the need for all .xib and .storyboard files and to have a simple set of rules each view must follow, in order to be created programmatically.

We also took into consideration that, with Apple’s 2019 SwiftUI release and what this means for developments in future, it made sense to adjust our workflow now so migrating to programmatic views is smoother in the future.

How do you create views programmatically?

To begin with, we need a way to make sure that all views follow the same set of rules so that any developer can pick up a client project and add a new view in a standardized way. Even if you’re not working in a team, this kind of standardization is still very important as it’ll make it easier to debug issues, and implement new views and features going forward.

For this, we’ve employed the assistance of a great little protocol that all of our views must conform to; View:

Note: This protocol will clash with the SwiftUI View protocol. So, this solution is solely for those using UIKit.

The protocol is designed to standardise the initial/setup part of your view’s lifecycle and make it much easier to understand and implement. It enables you to:-

  1. Initialise a view’s subviews
  2. Set up the subviews
  3. Set up the subviews accessibility identifiers
  4. Add the subviews to the view hierarchy
  5. Add constraints to the subviews and lay them out

It’s important to note that these functions must be called in this order when initialising your view. So, to make it even easier (if that’s possible!), there’s also an extension that’s used to get the ball rolling and trigger these functions.

This function means that all you have to do is call initialise() in every concrete class that conforms to the View protocol, so you don’t have to type out that boilerplate every single time.

Real-world example

Below is a real-world example of a UIView that conforms the View protocol used in our client apps. It’s the view used to display the price of a product and is used in multiple places throughout each app.

With our concrete class created, we can use it anywhere we like; just instantiate it and give it a price to display:

Note how we don't have to reference a .xib or .storyboard file to instantiate the view!

This gives you a view that looks something like this:

The outcome of the PoqPriceView example

(Well, it would if this was the final form of PoqPriceView. But we use some special styling magic, which we will talk about in another blog post).

Conclusion

That’s it! No need for a .xib or .storyboard file, no nightmarish merge conflicts to try and resolve, and an easy init/setup lifecycle.

If you like this post then stay tuned for the next one, where we improve on what we’ve done here by making it easy to style the view and its subviews, along with a couple of little helper extensions to boot!

--

--