A weekly blog about Swift and iOS

A Better Syntax for Configurable Initializations

Published on Jan 26, 2023

Note that this post has been republished from a previous blog, originally published on January 23, 2019

What is a configurable initialization?

First thing’s first, let’s define what I mean by configurable initialization.

Chances are you have tried to initialize a UIView but you needed to set some parameters on it, which resulted in something like the following:

let view: UIView = {
    let view = UIView()
    view.backgroundColor = .red
    return view
}()

This is a pretty practical use case in iOS development, but there is a bit of redundancy here.

Let’s see if we can make it better

Initializer extensions

To make this syntax a little cleaner, could we get rid of the initialization line and the return line? That way, we are just left with a line setting the background color.

Let’s create a class that takes any class of type UIView, and initializes it with some kind of configuration closure. We will start by initializing a new instance of the generic UIView or subclass, configure it, then return it:

final class Initializer<T: UIView> {
    static func initialize(configure: (T) -> Void) -> T {
        let t = T()
        configure(t)
        return t
    }
}

So far so good. This function does exactly what we did before, but it does it in a more generic way. This way, we can initialize a view like so:

class SomeView: UIView {
    var something: Int = 0
}

let someView = Initializer<SomeView>.initialize {
    $0.backgroundColor = .green
    $0.something = 0
}

Not only do we now have a cleaner way to initialize views, but we can do so for any subclasses of UIView also.

Let’s make it more generic

So we created a nice clean way of initializing UIView and subclasses of UIView but what about other classes? Also, I don’t know if I’m a fan of the new syntax we created. It forces developers to change the way they initialize objects, and it’s not very intuitive.

I think a cleaner solution would be to implement a custom initializer for all objects.

protocol InitConfigurable {
    init()
}
extension InitConfigurable {
    init(configure: (Self) -> Void) {
        self.init()
        configure(self)
    }
}

Not only is this a cleaner solution, but it actually makes more sense too. Any object that conforms to the InitConfigurable protocol must adhere to an init function, but also gets a free configurable initializer as an extension. The extension init calls the empty init and then configures itself with the given closure. It can be used like so:

class SomeView: UIView, InitConfigurable {
    var something: Int = 0
}

let someView = SomeView {
    $0.backgroundColor = .green
    $0.something = 1
}

But wait! There’s more!

Ok one last thing.

I really don’t want to have to adhere to InitConfigurable for every object that I want to be able to configure.

Here’s a simple solution that enables InitConfigurable for all NSObjects. Have fun configuring!

extension NSObject: InitConfigurable { }