Passing arguments to selector in Swift
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Selectors are a bridge between Swift and the Objective-C runtime, used heavily in UIKit's target-action pattern. When you add an action to a button or set up a timer, you reference a method by its selector. A common question that trips up developers is how to pass custom arguments through a selector, since the #selector syntax does not support arbitrary parameters directly. Understanding this limitation and the available workarounds is essential for writing clean UIKit code.
How Selectors Work
A selector is essentially a name that identifies a method at runtime. You create one using the #selector expression, which the compiler verifies at build time. The target-action pattern used by UIControl subclasses like UIButton only supports two method signatures for action methods: one with no parameters and one that receives the sender.
Notice that both methods are marked @objc. This is required because selectors rely on the Objective-C runtime, and Swift methods are not exposed to that runtime by default.
Why You Cannot Pass Custom Arguments Directly
The #selector expression only captures a method name. It does not capture or forward arguments. The target-action mechanism is designed so that UIKit itself calls your method, passing either nothing or the sender object. There is no built-in slot for additional data.
This means the following will not compile:
Workaround 1: Use the Tag Property
The simplest approach for passing an integer identifier is to use the tag property that every UIView has:
This works well when you have a list of buttons (for example, in a loop) and each button needs to carry a simple numeric identifier.
Workaround 2: Subclass UIButton to Hold Extra Data
When you need to pass more than a single integer, create a custom button subclass with additional stored properties:
This keeps the data directly on the sender, so the action method has full access without needing external lookups.
Workaround 3: Use a Closure-Based Approach
Starting with iOS 14, UIAction lets you attach a closure directly to a control, bypassing selectors entirely:
Because closures capture their surrounding scope, you can reference any local variable without needing tags or subclasses. This is the most modern and flexible approach.
Workaround 4: Use objc_setAssociatedObject
For cases where you cannot subclass the control, Objective-C associated objects let you attach arbitrary data to any NSObject:
Common Pitfalls
- Forgetting the
@objcattribute. Every method referenced by#selectormust be marked@objc, or the compiler will produce an error. If your class does not inherit fromNSObject, you also need to make it anNSObjectsubclass. - Over-relying on
tagfor complex data. Thetagproperty is only anInt. Trying to encode multiple values into a single integer leads to fragile code that is hard to maintain. - Using selectors in SwiftUI. SwiftUI uses closures and the Combine framework for event handling, not selectors. Attempting to use
#selectorin a SwiftUI view is a design mismatch. - Retaining self in closure-based actions. When using
UIActionclosures, capturingselfstrongly can create retain cycles if the button is owned by the same view controller. Use[weak self]when needed. - Misspelling the method signature. Before
#selectorwas introduced in Swift 2.2, selectors were string-based and typos caused silent runtime crashes. Always prefer the compiler-checked#selectorsyntax overSelector("methodName").
Summary
- Selectors in Swift reference method names at runtime and support either zero parameters or a single sender parameter.
- You cannot pass custom arguments through
#selectordirectly because the target-action pattern does not support it. - Use
UIView.tagfor simple integer identifiers, aUIButtonsubclass for richer data, orUIActionclosures (iOS 14 and later) to avoid selectors altogether. - Always mark selector-referenced methods with
@objcand ensure the class inherits fromNSObject. - In modern UIKit code, prefer closure-based
UIActionover selectors for cleaner, more flexible event handling.

