Swift Selector with default argument - swift

I have Write simple Code here
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: Selector("cancelClick"))
Actual Function
func cancelClick(isAlert:String = "yes"){
self.dismissViewControllerAnimated(true, completion: { () -> Void in
if isAlert == "yes" {
Functions.displayAlert("called")
}
})
}
self.cancelClick() - Worked but if i didn't pass the argument
self.cancelClick(isAlert:"no") - Crashed
So what should be my selector if i have to pass argument in default perameter
tried with both Selector("cancelClick") and Selector("cancelClick:") but no luck.

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.

Don't use Selector("cancelClick:"), try instead just "cancelClick:" with the colon so you can pass an argument.
And as Matt said, the argument you must pass is the sender (the button) itself.

Related

Trouble delaying function with variables passed to it in swift 4

I'm very new to swift and am having trouble with calling this function again on the 6th line after a delay, Xcode is telling me that
Argument of '#selector' does not refer to an '#objc' method, property, or initializer
I know what the problem is and have tried searching around but haven't been able to make anything work.
#objc func attemptToIdentify(_ user: String) {
if manager.status == .connected {
socket.emit("identify", user) //This functions correctly
} else {
print(manager.status, "... will attempt subscription in 1 second")
self.perform(#selector(attemptToIdentify(user)), with: nil, afterDelay: 1)
}
}
The problem is that attemptToIdentify(user) is a call. This is not the place for a call; you need a selector, i.e. the name of a function. So just put attemptToIdentify. If you need to pass something, you can pass it in the with parameter.
Even better, don't use perform:afterDelay: at all. If the idea is to add a delay to a call, just use asyncAfter (or my delay encapsulation of it).

Swift:"Unrecognized selector sent to instance", Xcode 9.4.1

Can anyone help me with this?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate=self
tableView.dataSource=self
searchBar.autocorrectionType = .yes
searchBar.delegate=self
searchBarView.translatesAutoresizingMaskIntoConstraints=false
let tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector(("tapCancel:")))
searchBarView.addGestureRecognizer(tap)
tableView.addGestureRecognizer(tap)
tableView.isUserInteractionEnabled=true
}
...
func tapCancel(sender:UITapGestureRecognizer){
hideSearchBarView()
isSearchon=false
print("Tap cancel!")
}
Every time I tap the View, it crash.
"unrecognized selector sent to instance 0x7feb85d109e0"
I will appreciate any help!
Thanks!
I believe the reason is due to how method names get mapped between Swift and Objective-C, which underlies the Cocoa implementation and the whole target/action mechanism.
In your case, the Swift method:
#objc func tapCancel(sender:UITapGestureRecognizer)
...corresponds to the Objective-C selector:
-tapCancelWithSender:
Note: In order to work with the target/action paradigm (i.e., called by means of a selector), the method needs to be declared as #objc. The alternative attribute #IBOutlet (for use in conjunction with Interface Builder) also supports this. (tip of the hat to #rmaddy)
In order to remove the "withSender" part and get a selector that matches tapCancel:, you need to tell Swift to remove the argument label sender, like this:
func tapCancel(_ sender:UITapGestureRecognizer) // Notice the underscore (_)
Also, in line with the comment by #dan, perhaps you can use:
#selector(self.tapCancel(_:))
or more succinctly, as pointed by (thanks again) #rmaddy, just:
#selector(tapCancel)
(Xcode will try to autocomplete it to #selector(tapCancel(_:)), but the shorter syntax works as well and the method name is highlighted)
I was not familiar with the Selector() syntax you used, so I tried playing a bit with it, and behold:
(Selector does not match any method the compiler can "see").
(After adding "withSender", the compiler can match the method, but it suggests using the better #selector(... syntax).
As #rmaddy also pointed out in the comments, using the shorter #selector(doSomething) syntax (no colons, no underscore, no self) also does away with the problem of whether "withSender" is needed or not.
use
let tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapCancel))
instead of
let tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector(("tapCancel:")))
And add #objc before method tapCancel()

Issue with types in swift?

I did a Team Treehouse code challenge, and I had an issue that I wanted to ask. Basically I created a func inside an enum that takes the specific case of the enum and initializes a given object. It told me to create it inside the enum, but unfortunately when I do this, I came across some errors. I was able to finish the challenge by doing it outside the enum, but I don't think that was exactly what I was supposed to do.
Anyways, my problem was that when it returned the object UIBarButtonItem, if it was assigned to a constant, the type of the constant is "() -> UIBarButtonItem" or depending on how the switch is setup, it could also come out like "(Button) -> UIBarButtonItem". I'm not really sure what the '->' symbol means in this case. Why does it not completely change into the class? What is going on here? In the tutorial right before this, it seems we did the same exact thing, so I'm not sure why it doesn't work.
enum Button {
case Done(String)
case Edit(String)
func toUIBarButtonItem () -> UIBarButtonItem {
switch self {
case .Done: return UIBarButtonItem(title: "Done", style: .Done, target: nil, action: nil)
case .Edit: return UIBarButtonItem(title: "Plain", style: .Plain, target: nil, action: nil)
}
}
}
let done = Button.Done("Done")
let doneButton = Button.toUIBarButtonItem(done)
This is the code that I am running, not including the UIBarButtonItem class. I hope this is enough information to understand what I am asking.
You are calling the toUIBarButtonItem() method on the Button type instead of a Button instance; this is why it does something special: it returns a curried function, which you then bind to done. The result, however, is still a function.
What you probably mean to do is invoke the method on your done object:
let done = Button.Done("Done")
let doneButton = done.toUIBarButtonItem()
A more in-depth explanation can be found under http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/

Dynamically performing an action based on string passed in textfield

Say I have a textfield - inputTextFieldand a button on UI with action performActionDynamically, in same class I define two functions: 1. firstFunc, 2. secondFunc, now I want to achieve this behavior:
If user types "firstFunc" in textfield and then he taps on button it should invoke firstFunc function, and if he types "secondFunc" in textfield and then he taps on button it should invoke secondFunc function.
In objective-c I would have easily achieved it by following below pseudocode:
Within performActionDynamically pass inputTextField.text in NSSelectorFromString() to obtain selector
Invoke performSelector:withObject: on self to perform respective function
The only thing which I can think in implementing same behavior in swift is -
Define two closures with name firstFunc, secondFunc
Store the closures in a dictionary as values for keys - 'firstFunc' and 'secondFunc' respectively
Obtain respective closure from dictionary based on value in textfield
Invoke obtained closure
Is there any better way to achieve intended behavior? Please guide.
You could try something like this:
if self.respondsToSelector(Selector(selectorString)) {
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(0), target: self, selector: Selector(inputTextField.text), userInfo: nil, repeats: false)
}
This is done as a callback, so you will have to take that into account.
You could use NSThread instead of NSTimer if you prefer:
NSThread.detachNewThreadSelector(Selector(inputTextField.text), toTarget: self, withObject: nil)

Selector string from Swift function

Given a function reference, is there a way in Swift to get the name of that function as a string suitable for passing to NSSelectorFromString?
I'd like to wrap NSNotificationCenter's addObserver with a version that takes a function reference instead of a selector string:
addObserver(self, function: someCallback, name: "some notification", object: nil)
But addObserver takes a String selector argument.
You're reinventing an unnecessary wheel. NSNotificationCenter already has an observation method that takes a function (what Objective-C calls a "block"):
addObserverForName:object:queue:usingBlock:
So just use that.
I'd still like to find an answer to my original question, but based on #matt's suggestion, I'm currently using this extension:
extension NSNotificationCenter {
class func addObserver(function: (NSNotification!) -> (), name: String? = nil, object: AnyObject? = nil, queue: NSOperationQueue? = nil) {
defaultCenter().addObserverForName(name, object: object, queue: nil, usingBlock: function)
}
}
Since it implicitly uses defaultCenter() and provides defaults for object and queue, which I'd almost always pass nil for, it allows for a more succinct call:
NSNotificationCenter.addObserver(doSomething, name: "some notification")
I like that it links against the actual function (doSomething), rather than a string representation. It's not a general purpose extension, but I think it covers 90% of the cases where I register for notifications.
Edit: I'm leaving this here for interest, but it's way too complicated (got wrapped up in how the question was asked, rather than the goal). Beyond the existing block-based approach, there's also this handy extension to it.
I wouldn't do it this way. It's too limiting because it would exclude function literals (anonymous functions).
Instead, I would play this game:
Create an dictionary property mapping [String: Void -> ()] (string to function)
When you register a new function, make up a unique, random key and store the function you're passed in your dictionary with that key.
Register with the selector observer_dispatcher_<key> (or whatever prefix you like).
Implement resolveInstanceMethod: to dynamically create any observer_dispatcher_ method you're requested. They can all point to the same IMP, something like:
(assuming this is good Swift; I haven't tried it):
void _observer_dispatcher(self: AnyObject, _cmd: Selector) {
let name = // strip the leading stuff off of _cmd
if let f = self.dispatchTable[name] {
f()
}
}
(This is still pretty sloppy; I'll have to think about the correct correct impl more, but this is the direction I'd go in.)