Swift — Leveraging the power of first-class functions

Jullian Mercier
2 min readFeb 16, 2020

Functions, in Swift, are first-class citizens that can be passed as arguments, stored in properties or returned from functions.

When dealing with Swift built-in functions such as `map` and `forEach`, one often ends up using the trailing closure syntax to provide with the required behavior.

let stackView = UIStackView()
let views = [UIView(), UIView(), UIView()]
views.forEach { view in
stackView.addArrangedSubview(view)
}

Or, using the $ syntax:

views.forEach {
stackView.addArrangedSubview($0)
}

While the above implementation is quite clear, let’s see how we can leverage the power of first-class functions and improve code readability.

— Functions

First, let’s take a closer look at the `forEach` expected function argument when looping over our views.

(body: (UIView) throws -> Void)

It expects a function that takes a `UIView` as as argument and returns `Void`.

Now, let’s look at the `addArrangedSubview` function from the Swift documentation.

func addArrangedSubview(_ view: UIView) -> Void

Interestingly enough, the `addArrangedSubview` method gives us exactly what we need to meet the `forEach` argument expectation giving us the ability to directly pass the `addArrangedSubview` into the `forEach` function.

views.forEach(stackView.addArrangedSubview(_:))

The wildcard function argument is not necessary since the compiler can figure on its own what method it refers to.

views.forEach(stackView.addArrangedSubview)

It provides with a shorter syntax while enhancing readability on a code that could almost be read as plain English text.

Let’s wrap it up by removing our subviews from our `UIStackView`.

views.forEach(stackView.removeArrangedSubview)

— Initializers

Since `initializers` are also functions that takes a certain amount of arguments and returns the object they’re defined in, it becomes quite convenient to use such declarative syntax to write more legible code.

let imageStrNames = ["flower", "car", "boat"]imageStrNames
.map(UIImage.init(named:))
.map(UIImageView.init)
.forEach(stackView.addArrangedSubview)

Notice how in the first `map`, we explicitly specify the argument label to give the compiler a hint on which `init` method we want to use (since a `UIImage` has several initializers, it could otherwise lead to misbehaviors).

struct Student {
let name: String
}
let studentNames = ["John", "Bob", "Peter"]
let students = studentNames.map(Student.init(name:))

Again, the compiler is smart enough to use the proper initializer so the explicit init argument `name` is not required.

let students = studentNames.map(Student.init)

Follow me on Twitter for my latest articles.

--

--

Jullian Mercier

Senior iOS engineer. jullianmercier.com. @jullian_mercier. Currently looking for new job opportunities.