TL;DR: Use closures to create modular and concise UI components in code.
There are countless ways to implement UI components in your app. Using Storyboards is a popular option, but there are times you’ll want to lay them out programmatically. In this post, I’ll show you iterative implementations of building the same UI, a simple login screen, programmatically. First, I’ll show you the simplest approach that’s quick to write but hard to maintain. Second, I’ll show you a better approach using private helper methods. Third, I’ll show you the best way to implement UI using closures. If you haven’t used closures before, you’ll quickly realize how powerful they are after reading this!
Before we dive into the programmatic approaches, I wanted to briefly touch on Storyboards. They’re ubiquitous in beginner iOS tutorials because they’re easy to visualize and understand. They’re also surprisingly powerful. You can drag and drop components, customize almost all aspects of a UI component (e.g., font size, background color) in real-time, and test what your UI will look like on various device sizes and orientations.
However, there are also times when you want to create your UI programmatically, either partially or completely. For me, it was working with other iOS developers who preferred building UI exclusively in code. Storyboard files can definitely become a headache really quick when working on a shared iOS codebase. As I’ve been writing programmatic
UIButton, and other core UI components, I of course wanted to learn what are best practices for implementing them. Below are three examples of implementing a simple login screen that illustrate considerations when writing programmatic UI code.
Let’s say we wanted to create this screen:
Here we have a title label, a caption label, and two buttons. How can we make this programmatically? Well, the simplest approach is to just write it all in
This is truly the simplest way to go about this, but that’s where the benefits end. What if I wanted another button? You’d have to name it and write a lot of repetitive code. What if I need to rename a label? Then you have to find and replace all instances of that name. I didn’t even include the layout code and you can see how this gets long very quickly! Not only that, it pollutes the
viewDidLoad() function which we want to avoid as our project grows. Maybe we can use private helper functions?
viewDidLoad() looks way cleaner! We refactored our label and button implementations into helper functions that are both
private (to restrict access) and reusable (e.g.,
createLabel(text:textColor:font:) is used to create both title and caption labels). This is better, but there are still some weaknesses.
First, if we want our UI components to have lots of customizations, the init parameters start to become lengthy and unscalable. In the code snippet above, the
createButton(text:textColor:font:backgroundColor:) method has four inputs, one of which has an optional default value. As our buttons get more complicated, we want to weigh the benefit of reusable, helper functions versus bloated, catch-alls that are hard to understand at first glance. Though this function follows the DRY principle, we sacrifice readability (linters will probably flag this as being too long if we add another parameter).
In our third implementation, we’re going to use something different: closures. Closures are encapsulated blocks of functionality that pop up in a number of scenarios. In fact, you’ve been using closures all along–functions (global and nested) are a kind of closure! It’s just a more abstract term for it. A closure expression, for instance, can be stored as a variable and executed when read.
Closures are awesome and worth reading up on (see Apple’s docs). You can do a lot with them and keep your code modular. We’re going to use them to create our UI components and I’ll explain why this is a great approach:
First, notice how concise our
viewDidLoad() is now! We don’t need bloated function calls anymore to create customized buttons. Second, notice how readable and concise the closures are: each of them instantiates itself and returns itself in curly brackets. Closures in Swift are smart about inferring types–we’re using their syntactic sugar to the max here–so we can remove the boilerplate that comes with explicitly writing out the type signature for closures (read this post for details). Third, we can easily rename labels and buttons–all we do is rename the variable! Fourth, the implementation inside the closures are pretty generic and can be copy and pasted for other use cases easily. Over time, you can build out your own library of UI components that you reuse for multiple iOS apps. The possibilities are endless!
I hope you learned a thing or two about closures and why they’re the way to go for programmatically creating UI components. If you want to test it out yourself, I put up a demo app with the closures implementation here. I was inspired by this blog post so check it out to learn more. Thanks for reading!