Keeping code in scope using the Applicable Protocol


Whenever I write code that’s mutating properties or calling methods on an initialized objects / creating new instances of an object, I like to wrap it in a closure to build a coherence between the object and the changes made to it, also I think it adds a very good amount of readability.

There’s a Swift Protocol I’ve created for this which I called Applicable as its one and only method is called apply. It’s heavily inspired by one of the Kotlin Scope Functions (which, surprise surprise, is also called apply).

Let’s assume you want to instantiate a UILabel and modify some of its properties. The obvious thing to do probably looks like this code without using the Applicable Protocol.

let label = UILabel()
label.text = "Please enter your name!"
label.backgroundColor = .white
label.layer.cornerRadius = 3.2
label.layer.masksToBounds = true
label.textAlignment = .center
label.textColor = .systemBlue
label.font = UIFont.boldSystemFont(ofSize: 12.0)

let anotherLabel = UILabel()
anotherLabel.text = "What's your date of birth?"
anotherLabel.backgroundColor = .systemGray
anotherLabel.highlightedTextColor = .systemRed
anotherLabel.font = UIFont.systemFont(ofSize: 12.0)

While this is ok(ish) code to me it doesn’t look particularly nice, especially if there are even more objects that are being modified like this. I guarantee that most developer have at least seen this once in their careers.

Now to make this code more readable and also help the next developer, who’s picking this up, get on speed as quickly as possible, I’m usually using the Applicable Protocol.

let label = UILabel().apply { label in
    label.text = "Please enter your name!"
    label.backgroundColor = .white
    label.layer.cornerRadius = 3.2
    label.layer.masksToBounds = true
    label.textAlignment = .center
    label.textColor = .systemBlue
    label.font = UIFont.boldSystemFont(ofSize: 12.0)
}

let anotherLabel = UILabel().apply { label in
    anotherLabel.text = "What's your date of birth?"
    anotherLabel.backgroundColor = .systemGray
    anotherLabel.highlightedTextColor = .systemRed
    anotherLabel.font = UIFont.systemFont(ofSize: 12.0)
}

At first-glance it doesn’t probably seem to do or change too much, however to me having all code where it belongs is vital to be able to maintain an App over a longer period of time, as well as working with other developers. But the Protocol is even more powerful and makes your code even nicer in other scenarios. Let’s take a look at returning and configuring a cell for a UITableView the usual way:

func createCell(at indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    cell.textLabel?.text = "Lorem ipsum cell ipsum"
    cell.detailTextLabel?.text = "This is a detail text"
    return cell
}

No with the Applicable Protocol this could look like the following code.

func createCell(at indexPath: IndexPath) -> UITableViewCell {
    return tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath).apply { cell in
        cell.textLabel?.text = "Lorem ipsum cell ipsum"
        cell.detailTextLabel?.text = "This is a detail text"
    }
}

There’s really a ton of use-cases for this, it mostly is probably also a matter of taste and code-style, but in my opinion it can help to improve certain code significantly, my main use case has been styling UI in code.

The Protocol itself is available as a Gist at GitHub.

Thanks for reading and if there’s any ideas, comments or you just want to get in touch, please feel free to get in touch via Twitter or Mail.

Cheers, Marcus