Translate Optional Delegate Protocol Methods to Swift Block-based Event Handlers Using Nested Objects

Found this little gem in a library called NextGrowingTextView, shortened to make the point clear with less code. It’s a collection of block-based event handlers in a little class:

public class Delegates {
    public var textViewDidBeginEditing: (NextGrowingTextView) -> Void
    public var textViewDidEndEditing: (NextGrowingTextView) -> Void
    public var textViewDidChange: (NextGrowingTextView) -> Void

    public var willChangeHeight: (CGFloat) -> Void
    public var didChangeHeight: (CGFloat) -> Void
}

Update 2023-12-20: Re-reading this today, I notice I never mentioned that I’d rather use a struct; there’s no benefit of using a reference type object if all it does is own the closures. public struct Delegates it is, then! By the way, they removed the Delegates type in the meantime, starting with a deprecation in 2017.

So there’s a bunch of traditional delegate callbacks – only they are not defined as protocol requirements but as properties of an otherwise state-less type.

You use it like this:

let growingTextView: NextGrowingTextView

growingTextView.delegates.textViewDidChange = { (growingTextView: NextGrowingTextView) in
    // Do something
}

Many views I write have less than 5 event callbacks. But if you write a complex component with many interactions or lots of decision-making, then it might be a good idea to wrap these up in a Delegates sub-type:

class BananaComponent: UIView {
    // ...
    let delegates = Delegates()
    
    class Delegates {
        // Favor "no-op" defaults over optionals
        var bananaComponentDidShow: (BananaComponent) -> Void = { _ in }
    }
}