Swift Selector with Default Argument

How to add a default parameter value in the #selector

One solution is to use a helper function that is separate from the selector function. The selector function can call the helper function, which can have as many parameters as you wish.

Adding the target:

slider.addTarget(self, action: #selector(handleChange(_ sender: UISlider)), for: .valueChanged)

Target function:

func handleChange(sender: UISlider) {
// logic based on the action or slider
doSomething(isShown: true)
}

Helper function:

func doSomething(isShown: Bool) {
// make your changes using the parameters here
}

Swift Selector with default argument

The thing is that the parameter is not up to you. It is always the button (the "sender"), and that is the only thing it can be.

In other words, if you want this function to have a parameter, then by all means you will need to set your selector string as "cancelClick:" - the colon means that it takes a parameter. But that parameter must be the button:

func cancelClick(bbi:UIBarButtonItem?) {

However, you will notice that I have cleverly made this UIBarButtonItem parameter an Optional. Why do you think I did that? Because now you can also call it directly and pass nil:

self.cancelClick(nil)

Thus, cancelClick: now has a way to know whether the call comes from the tapping of a button or by a direct call - if bbi is not nil, the button was tapped; if bbi is nil, we were called directly from code. Sneaky, eh?

Another sneaky approach is to make the parameter an AnyObject:

func cancelClick(sender:AnyObject) {

The beauty of this is that you can call it with any kind of class instance. cancelClick can check the type of the sender. If it is a UIBarButtonItem (sender is UIBarButtonItem), then we were called by tapping the button. Otherwise, if called in code, you can pass in a string or anything else that this function might be prepared to deal with.

Why aren't default parameters for functions respected when called via Selector in Swift?

Default parameters are inserted by the Swift compiler when you call the function.

So this is a compile-time thing.

When manually calling the function via a selector, you use the Objective-C runtime, which has no idea about your default parameters.

This is a runtime thing.

Moreover, default parameters don't exist in Objective-C.

You can think of Swift default parameters as a compile-time convenience.

But once you run your code, they're basically gone.

EDIT

If you're looking for a workaround, I can suggest having two different functions, without using default parameters:

@objc func printValue()
{
printValue(value: 7)
}

@objc func printValue(value:Int)
{}

This way, you can call it without arguments through the Objective-C runtime.
Note that when using #selector, you'll need to cast to resolve ambiguity:

self.button?.addTarget(self, action: #selector((printValue as () -> Void)), for: .touchUpInside)

Swift default parameter & Selector Syntax

You're getting a crash because it expects a different parameter. 'addTarget' expects to get a function who's sole parameter (if any) is AnyObject (or maybe UIView), not Bool

swift functions with default parameters also a selector?

That's right that you are getting sender object which is actually UIBarButtonItem. Have you heard about Target-Action Cocoa pattern? If no, you can read more here:
https://developer.apple.com/library/ios/documentation/General/Conceptual/Devpedia-CocoaApp/TargetAction.html

Especially relevant section to you is "An Action Method Must Have a Certain Form".

Consider to introduce addWebView overload:

func addWebView(sender: NSObject) {

addWebView(url: NSURL(string: "https://www.google.com")!)
}

private func addWebView(url: NSURL) {
//left as is ...
}

Here is update per Dave's comments.
Have to use different name for actual implementation method. Otherwise Swift compiler is failed to resolve the assigned selector name.

Useful code, which demonstrates the problem is attached below:

class Notifier: NSObject {

private var _target: NSObject!
private var _action: Selector!

func addObserver(target: NSObject, action: Selector) {
_target = target
_action = action
}

func invokeMethod() {

guard let t = _target else {
print("target must be set")
return
}

guard let a = _action else {
print("action must be set")
return
}
if t.respondsToSelector(a) {
t.performSelector(a, withObject: self)
}
}

}

class Observer: NSObject {

func subscribe(notifier: Notifier) {
notifier.addObserver(self, action: #selector(Observer.callback))
}

func callback(sender: NSObject) {
callbackImpl(NSURL(string: "https://www.google.com")!)
}

private func callbackImpl(url: NSURL) {
print("url\(url)")
}
}

//client's code
let n = Notifier()
let o = Observer()
o.subscribe(n)
n.invokeMethod()

Swift: Passing a parameter to selector

#selector describes method signature only. In your case the correct way to initialize the selector is

let selector = #selector(moveToNextTextField(tag:))

Timer has the common target-action mechanism. Target is usually self and action is a method that takes one parameter sender: Timer. You should save additional data to userInfo dictionary, and extract it from sender parameter in the method:

func moveToNextTextField(sender: Timer) {
print(sender.userInfo?["tag"])
}
...
let selector = #selector(moveToNextTextField(sender:))
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: selector, userInfo: ["tag": 2], repeats: false)

Passing arguments to selector in Swift

It looks like you're misunderstanding a couple of things.

When using target/action, the function signature has to have a certain form…

func doSomething()

or

func doSomething(sender: Any)

or

func doSomething(sender: Any, forEvent event: UIEvent)

where…

The sender parameter is the control object sending the action message.

In your case, the sender is the UITapGestureRecognizer

Also, #selector() should contain the func signature, and does NOT include passed parameters. So for…

func handleTap(sender: UIGestureRecognizer) {

}

you should have…

let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))

Assuming the func and the gesture are within a view controller, of which modelObj is a property / ivar, there's no need to pass it with the gesture recogniser, you can just refer to it in handleTap

Swift: Default function parameter value via NotificationCenter

The correct way is

@objc func myFunc(_ notification:Notification) // OR NSNotification also

Don't expect the NotificationCenter.default to set a value for theFlag: Bool = false , with

NotificationCenter.default.addObserver(self, selector: #selector(myFunc), name: MyNotificationName, object: nil)

OR

NotificationCenter.default.addObserver(self, selector: #selector(myFunc(_:)), name: MyNotificationName, object: nil)

//

If you want to send a value then insert it inside object / userInfo

NotificationCenter.default.post(name:MyNotificationName, object:<#Here#>, userInfo:<#OrHere#>)

Difficulties to assign default value to a parameter of a function

I don't think that is possible. The default value is inserted at the calling site, and therefore needs to be public, see also
Access control in swift 4.

A possible workaround would be to make the parameter optional,
and substitute nil by the default value locally:

class Foo {
private static let DefaultValue = 10

public func doTask(amount: Int? = nil) {
let amount = amount ?? Foo.DefaultValue
// ...
}
}


Related Topics



Leave a reply



Submit