Closure based configuration

Motivation:
While the Swift Book does an excellent job introducing closure expressions, trailing closures, and functional patterns like sorting and mapping, it currently lacks coverage of a practical and idiomatic use case: closure-based object configuration.

This pattern is widely used in UIKit and other Swift frameworks to initialize and configure objects inline. It leverages immediate execution and shorthand syntax ($0) to reduce boilerplate and improve readability. For example:

var label: UILabel = {
    $0.text = "Swift Book"
    $0.textColor = .darkGray
    $0.font = UIFont.systemFont(ofSize: 16)
    return $0
}(UILabel())

This approach offers several benefits:

  • :white_check_mark: Real-world relevance: Commonly used in Swift UI codebases, especially UIKit.

  • :white_check_mark: Teaches immediate execution closures: A subtle but powerful concept not yet covered.

  • :white_check_mark: Expands closure usage: Helps developers see closures as more than just callbacks or functional tools.

  • :white_check_mark: Complements existing content: Builds on the reader’s understanding of closure syntax and execution.

Adding this example would help bridge the gap between theoretical closure usage and practical Swift development, especially for developers transitioning from other languages or learning Swift through UI work.

I’d love to hear your thoughts—especially on whether this fits the scope of TSPL and if a SwiftUI variant would be valuable to include.

P.S: I have learned this recently and didn’t found official document, otherwise I would have used it before :slight_smile:

1 Like

I've definitely done something similar before:

let label: UILabel = {
  let label = UILabel()
  label.text = "Swift Book"
  return label
}()

This is just a translation of the IIFE pattern from JavaScript.

I've found that anyone who tried this stopped using it. The most common alternative seems to be adding extensions to protocols. I use operators to avoid the protocols. Can you provide analysis? I am interested to know what the entirety of standards on Earth are.

A slightly more elegant version is to extend NSObject with a then function that provides the closure. I use that for UIKit view building in properties sometimes, but it's really just a stylistic choice. That said, I'm not sure the Swift book needs to cover all possible solutions in the language.

If I had to do something like this I'd probably Swift-UI-cate it:

let label = UILabel()
    .text("Swift Book")
    .font(.title)

There's no magic in Swift to treat foo.bar(expression) the same way as foo.bar = expression (†) so it would be manual boilerplate.


† - compare to Obj-C: [foo setBar: expression] ⇆ foo.bar = expression, however the former is not quite ready to be used in a chained manner as it's returning void and it won't chain nicely anyway: [[foo setBar: expression] setBaz: another].

I've certainly never seen it written exactly like the OP; I use this pattern with DateFormatters a lot:

let dateFormatter = {
    let formatter = DateFormatter()
    formatter.property = value
    ...
    return formatter
}()

passing the object into the configuration closure as an argument seems unnecessarily arcane?

1 Like

A free function is the same as my operator approach.

let label = with(UILabel()) { $0.text = "Swift Book" }
public func with<Object: AnyObject, Error>(
  _ object: Object,
  transform: (Object) throws(Error) -> Void
) throws(Error) -> Object {
  try transform(object)
  return object
}

Maybe there's a good idea to be had with parameter packs. I don't like this, and so doubt it:

let label = with(
  UILabel(),
  (\.text, "Swift Book"),
  (\.font, .preferredFont(forTextStyle: .title1))
)
public func with<Object: AnyObject, each Value>(
  _ object: Object,
  _ transform: repeat (ReferenceWritableKeyPath<Object, each Value>, each Value)
) -> Object {
  (repeat { object[keyPath: $0.0] = $0.1 } (each transform))
  return object
}

Big thread here: [Pitch] `With` functions in the standard library

2 Likes

Thats a builder pattern. Having closure-based object configuration can have many possibilities of encapsulating code, Single point of failure, readability, etc, Lets say.. We want to draw a horizontal stack view with two label,

    let horizantalStackView: UIStackView = {
        $1.setText("Swift")
        $2.setImage("book").setContentMode(.scaleAspectFit)
        $0.setAxis(.horizontal).addArrangedSubview($1).addArrangedSubview($2)
        return $0
    }(UIStackView(), UILabel(), UIImageView())

This example is supporting your builder pattern, but it has other added advantage,
like

  1. local reasoning
  2. With closure based object initialization, you are aware upfront about which object you are working in given context.
  3. It encapsulate code chunk.

This doesn’t provide information on upfront objects which we will working on, given when there is a set of object like this example:

let horizantalStackView: UIStackView = {
        $1.setText("Swift")
        $2.setImage("book").setContentMode(.scaleAspectFit)
        $0.setAxis(.horizontal).addArrangedSubview($1).addArrangedSubview($2)
        return $0
    }(UIStackView(), UILabel(), UIImageView())

Not only this, We are not creating additional functions to support this behaviour. Swift has this capabilities which is not discussed so far in swift book.

What if, Lets say
we wants to work with HorizontalStackView, with One label and One ImageView.

let horizantalStackView: UIStackView = {
        $1.setText("Swift")
        $2.setImage("book").setContentMode(.scaleAspectFit)
        $0.setAxis(.horizontal).addArrangedSubview($1).addArrangedSubview($2)
        return $0
    }(UIStackView(), UILabel(), UIImageView())

This gives us power to think locally, upfront awareness about objects which we gonnaa work on, encapsulations etc.

Both examples just closures, one being type of (UILabel) -> UILabel and the other () -> UILabel that are immediately executed, so it's about closure expressions and evaluation timing, not a builder pattern.

I would suggest not to focus on patterns (especially OOP ones) and step up a bit and look broader — closure in Swift is basically a lambda expression and one of the building blocks of the language, so you can use it however you want. It's pretty common in functional programming languages (I mean, it's building block) and here where I think Swift Book doing everything correct — those who know immediately recognize it, but for newcomers it's not overwhelming.

1 Like

Thanks, Jaleel—really appreciate the clarity and the broader framing.

You're absolutely right: this is fundamentally about closure expressions and evaluation timing, not about enforcing a builder pattern per se. My initial framing leaned on the builder analogy just to make the pattern relatable for folks coming from OOP-heavy backgrounds, but I agree that anchoring it in Swift’s functional foundations is more appropriate.

I like your point about Swift Book doing a good job of keeping things approachable for newcomers. My intent with the proposal was to surface a real-world idiom that many UIKit developers use, especially when configuring views inline, showing how closures—already covered in TSPL—can be applied in practical, expressive ways.

Would you be open to a small addition that frames this as an example of immediate closure execution for object configuration, without invoking any design pattern terminology? That way, it stays grounded in Swift’s language features and avoids overloading the reader with abstraction.

That is too many numbers. Like I said, people stopped using this because they realized nobody liked trying to understand multiple shorthand numbers.

I like the set-style syntax alright, but there's no precedent for it being added for every possible mutation or method call.

I like dots but I don't care what the operator actually is. I don't have hope that Swift will ever get anything to solve the problem, but an operator is the best choice available now because of parentheses wrapped around big chunks otherwise.

let horizontalStackView = UIStackView(
  arrangedSubviews: [
    UILabel()..{ $0.text = "Swift" },
    UIImageView()..{
      $0.image = .init(named: "book")
      $0.contentMode = .scaleAspectFit
    }
  ]
)..{ $0.axis = .horizontal }
infix operator ..

public func .. <Object, Error>(
  _ object: Object,
  transform: (Object) throws(Error) -> Void
) throws(Error) -> Object {
  try transform(object)
  return object
}

For prior art, see the Kotlin functions also and apply. The former is something like this, though I think this isn't valid Swift because you can't write an extension on Any:

extension Any {
  @inline(__always)
  public consuming func also<E: Error>(_ block: (Self) throws(E) -> Void) throws(E) -> Self {
    block(self)
    return self
  }
}

In order to get that compiled I had to implement some obvious boilerplate

Hidden
extension UILabel {
    @discardableResult func setText(_ text: String) -> UILabel { self }
}

extension UIImageView {
    @discardableResult func setImage(_ name: String) -> Self {
        // TODO the actual logic
        return self
    }
    @discardableResult func setContentMode(_ mode: UIControl.ContentMode) -> Self {
        // TODO the actual logic
        return self
    }
}

extension UIStackView {
    @discardableResult func setAxis(_ axis: NSLayoutConstraint.Axis) -> Self {
        // TODO the actual logic
        return self
    }
    @discardableResult func arrangedSubview(_ view: UIView) -> Self {
        // TODO the actual logic
        return self
    }
}

I could see how using that could be useful (fluent / chained style, etc).

However what you wrote:

    let horizantalStackView: UIStackView = {
        $1.setText("Swift")
        $2.setImage("book").setContentMode(.scaleAspectFit)
        $0.setAxis(.horizontal).addArrangedSubview($1).addArrangedSubview($2)
        return $0
    }(UIStackView(), UILabel(), UIImageView())

is just a fancier version (besides being slightly longer) than what we could write now:

    do {
        let (_$0, _$1, _$2) = (UIStackView(), UILabel(), UIImageView())
        _$1.setText("Swift")
        _$2.setImage("book").setContentMode(.scaleAspectFit)
        _$0.setAxis(.horizontal).arrangedSubview(_$1).arrangedSubview(_$2)
    }

I maintained the formatting to make it obvious how the two compare.

You don't really want to do all that work in a property initializer anyway. Usually we limit them to configuring a single view, then do the actual layout work in viewDidLoad or another lifetime event.

Hello @Danny ,
Thanks for response.
While the original proposal focused on closure-based configuration, (Which is our context)
this operator-based approach solves the same problem more cleanly. It avoids numbered shorthand overload, keeps syntax readable, and works well in nested view hierarchies. It’s not about introducing new patterns—it’s about making existing idioms more ergonomic.:slightly_smiling_face:

Hello @Jon_Shier ,
Thanks for the response,

While the original proposal focused on closure-based configuration,
We are not discussing on iOS specific or how we can write code which can offload work from View life cycle and call it there. :slight_smile:

Hello @tera ,

Thanks for your response,
While the original proposal focused on closure-based configuration,

Thanks for laying that out so clearly—the side-by-side formatting really helps. You're right: the tuple-based do block is just as expressive and arguably more readable, especially when shorthand arguments start piling up. I appreciate the reminder that Swift already gives us clean ways to batch object setup without needing closure gymnastics.

My original example leaned on closure execution to keep everything inline, but I agree that once you go beyond one or two parameters, the readability drops fast. The tuple unpacking approach avoids that cognitive load and keeps things explicit.

to get to something like this (following your sample):

    class MyView or ViewController: ... {

        let titleLabel = UILabel()
        
        lazy var horizantalStackView = UIStackView(axis: .horizontal) {[
            titleLabel,
            UILabel()
                .text("Swift"),
            UIImageView()
                .image("book")
                .contentMode(.scaleAspectFit)
        ]}

        func elsewhere() {
            titleLabel.text = ...
        }
    }

Notice the absence of variable names (neither $0 nor some explicit names) as there is no need to have those other than the ones being referenced elsewhere (in this case these are titleLabel and horizantalStackView. I had to make horizantalStackView lazy as otherwise I wouldn't be able referencing titleLabel in its initialiser. The trailing closure parameter of a custom UIStackView initialiser in this case is a non-escaping callback that returns an arrays of sub-views. This looks (and feels) SwiftUI, the difference is that the view hierarchy is not rebuild when something changed.