Pass arguments to a Selector with Swift

Sometimes when building an application it would be useful to pass arguments to a selector. In my tutorial on hiding tableview sections, this would have been useful, but instead I had to use a tag.

Selectors do not work work the same way that a normal function/method would work, which, in this case means that we cannot pass arguments in the same way, because the arguments work differently. So in this tutorial we will go through a technique to pass whatever data you need when using selectors.

To do this we can subclass the type that we want to add a target to. In this case, we would create a subclass of UIButton, we would then be able to add variables and functions to it to do and store whatever we want. This works because when we use a selector it will pass the sender through as an argument, in this case the sender will be the button.

By creating a custom button we will be able to pass any kind of data using the sender argument. Let's see how this works in practice, we will be using a tap gesture, but a button or a timer will work in the same way.

In this example we will use a tap gesture. We need to create a subclass of UITapGestureRecognizer so that we can add our own property on it, ourCustomValue. We will then create our selector which will be called when we tap on the view. Inside this selector we will access the value stored in the property of our subclassed UITapGestureRecognizer.

Let's create our UITapGestureRecognizer subclass first, add the following to your code:

class CustomTapGestureRecognizer: UITapGestureRecognizer {
    var ourCustomValue: String?
}

CustomTapGestureRecognizer is our subclass of UITapGestureRecognizer. All we have done is add a property called ourCustomValue which is a string.

Next let's add our selector. Add the following to your view controller:

@objc
func tapSelector(sender: CustomTapGestureRecognizer) {
    print(sender.ourCustomValue)
}

The last thing that we need to do is to use our CustomTapGestureRecognizer. Add the following code to your viewDidLoad:

let tapGesture = CustomTapGestureRecognizer(target: self,
                                            action: #selector(tapSelector(sender:)))
tapGesture.ourCustomValue = "This is a value we can set"
        
self.view.addGestureRecognizer(tapGesture)

You should now be able to build and run the app, if you tap on the view you will find that Optional("This is a value we can set") gets printed in the console, which is what we expect.

So this is how one will go about "passing arguments to a selector", you have to create a custom type that can handle the data that you want to store.

This is the full source code:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tapGesture = CustomTapGestureRecognizer(target: self,
                                                    action: #selector(tapSelector(sender:)))
        tapGesture.ourCustomValue = "This is a value we can set"
        
        self.view.addGestureRecognizer(tapGesture)
        
        // Do any additional setup after loading the view.
    }
    
    @objc
    func tapSelector(sender: CustomTapGestureRecognizer) {
        print(sender.ourCustomValue)
    }
}

class CustomTapGestureRecognizer: UITapGestureRecognizer {
    var ourCustomValue: String?
}