Swift
UIButton
Closures
Target-Action
iOS Development

Hooking up UIButton to closure? Swift, target-action

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

UIKit buttons are traditionally wired with target-action selectors, but many teams prefer closure-based handlers for readability and local behavior definition. The challenge is adding closures without breaking UIKit lifecycle semantics or introducing retain cycles.

This article shows a safe closure wrapper pattern for UIButton.

Core Sections

1) Traditional target-action baseline

swift
1button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
2
3@objc private func didTap() {
4    print("tapped")
5}

This is stable and fully UIKit-native.

2) Closure wrapper via associated object

swift
1import UIKit
2import ObjectiveC
3
4private var actionKey: UInt8 = 0
5
6final class ClosureSleeve {
7    let closure: () -> Void
8    init(_ closure: @escaping () -> Void) { self.closure = closure }
9    @objc func invoke() { closure() }
10}
11
12extension UIControl {
13    func on(_ event: UIControl.Event, _ closure: @escaping () -> Void) {
14        let sleeve = ClosureSleeve(closure)
15        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: event)
16        objc_setAssociatedObject(self, &actionKey, sleeve, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
17    }
18}

Associating the sleeve prevents it from being deallocated.

3) Usage

swift
button.on(.touchUpInside) { [weak self] in
    self?.submit()
}

Use weak capture for owner references to avoid cycles.

4) Multiple events and replacement behavior

If you need multiple handlers, store an array of sleeves instead of one object key. Otherwise later calls can overwrite previous handlers.

5) When to prefer selectors

For reusable components and objective-c interoperability, selectors may remain clearer. Closure helpers are best for localized view-controller logic.

6) Production checklist for UIButton closure wiring

To move this pattern from tutorial code into dependable production behavior, define a repeatable validation workflow before rollout. Start with three explicit acceptance metrics: correctness, reliability, and latency. Correctness should be measured against known fixtures or golden outputs, reliability should include error-rate and retry outcomes, and latency should use tail metrics such as p95 or p99 rather than simple averages. Running these checks once locally is not enough; they should execute in CI and, when possible, in a staging environment that resembles production data volumes and dependency behavior.

Next, capture environmental assumptions where maintainers can see them. Document runtime version, library versions, required environment variables, and external service dependencies. Many regressions happen because one assumption changes silently: a runtime upgrade, a minor package update, or a different default configuration in a deployment environment. Add at least one negative test that simulates a realistic failure mode, such as timeout, malformed input, permission issue, or missing artifact. These tests verify that failure handling is explicit and observable rather than hidden.

Operational readiness also requires ownership and rollback clarity. Define who responds when this component fails, what threshold triggers investigation, and what rollback path can be executed quickly. If the feature can be gated, prefer a flag-driven rollout so you can disable behavior without emergency code changes. Even for small utilities, this discipline prevents long incident timelines.

bash
1# Example pre-release validation sequence
2make lint
3make test
4./scripts/smoke_check.sh

Finally, keep a brief limitations note. State clearly what this implementation handles and what it intentionally does not optimize. That helps future contributors avoid accidental misuse and keeps design decisions grounded in explicit tradeoffs. Revisit this checklist after major framework or infrastructure upgrades, because behavior that was safe under one runtime may degrade under another if assumptions are no longer valid.

Common Pitfalls

  • Forgetting associated-object retention and losing callback execution.
  • Capturing self strongly inside closure and creating memory leaks.
  • Overwriting previous closure handler when supporting multiple events.
  • Mixing selector and closure wiring without clear ownership.
  • Hiding complex business logic inside UI callback closures.

Summary

Closure-based button actions can be clean and safe when implemented with retained wrapper objects and disciplined capture lists. For large reusable components, selectors may still be preferable. Use the approach that keeps event wiring explicit and maintainable.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.