Refactoring in Swift: Setup Closures

A quick idea on how a simple function can make your setup closures much neater

Max Chuquimia
ITNEXT

--

Succinct!

A classic sight at the top of a view controller is a list of views being instantiated and some properties being set on each:

let contentView: UIView = {
let v = UIView()
v.backgroundColor = .red
v.clipsToBounds = true
return v
}()
let titleLabel: UILabel = {
let l = UILabel()
l.font = .systemFont(ofSize: 22)
l.textColor = .red
l.text = "WELCOME"
return l
}()
let stackView: UIStackView = {
let s = UIStackView()
s.axis = .vertical
s.distribution = .fillEqually
s.alignment = .center
s.spacing = 8
return s
}()

When I look at that code, I can’t help but feel that there’s some redundant and duplicate information, specifically:

So, I created a function that can be used to make setup closures a lot neater:

func create<T>(_ setup: ((T) -> Void)) -> T where T: NSObject {
let obj = T()
setup(obj)
return obj
}

This function sits above everything and can instantiate any NSObject subclass that doesn’t need arguments passed to it’s init. Now if we were to re-write our setup closure list it will only have the bare minimum of scaffolding the compiler requires:

let contentView: UIView = create {
$0.backgroundColor = .red
$0.clipsToBounds = true
}
let titleLabel: UILabel = create {
$0.font = .systemFont(ofSize: 22)
$0.textColor = .red
$0.text = "WELCOME"
}
let stackView: UIStackView = create {
$0.axis = .vertical
$0.distribution = .fillEqually
$0.alignment = .center
$0.spacing = 8
}

This works by taking advantage of the following Swift language features:

  • A function’s return value can be generically inferred by the type it is being assigned to, for example func x<ReturnType>() -> ReturnType
  • The last closure argument of a function can be written as a closure outside of the function, for example array.forEach { … }
  • Closure arguments don’t need to be named; it’s possible to use $0 instead, for example array.compactMap { $0 }

So now the property’s type only needs to appear once and we don’t need to create a variable in the scope of a closure that we call immediately!

--

--

Writer for

Principal Software Engineer, Electronics Enthusiast, Appreciator of Music