how do I make a selector in swift for a method with parameters

I have this code which operates properly:


class aController: NSWindowController {

func aMethod () {

....

}

}


class aTest {

var myController: aController()

func test {

let methodName: String = "aMethod"

let method = Selector (methodName)

if myController.responds (to: method) {

myController.perform (method)

}

}

}


In this case, when I invoke method test of class aTest, tyhe method of class aController is always invoked.

But if the method "aMethod" has parameters, I don't kown how to write my code:


I have tested many possibilities, but it doesn't work

Someone to help?

Type "Selector" has a constructor with a String parameter because ultimately Obj-C selectors are based on method names. However, current versions of Swift use a type-safe syntax to construct selectors. If your method has parameters, like this (for example):


     @objc func aMethod (_ firstParameter: Int, secondParameter: Int)


You would construct the selector like this:


     #selector (aController.aMethod (_:secondParameter:))


This example illustrates all the requirements: the function must be @objc, the function name must be qualified (made unambiguous, anyway) by the type within which it is defined (you can use a variable of that type in this syntax, if you wish), and the parameters must be listed (using the external names, and specifying "_" for parameters without external names).


However, if the method name is unambiguous without giving the parameters, then you don't have to list them in the selector. In the above case, this works, too:


     #selector (aController.aMethod)


and means the same thing.


FWIW, you can still constructors using strings, but you need to know what the Obj-C version of the method name is going to be. For example, with this function:


     @objc func aMethod (a:Int)


the correct selector would be Selector ("aMethodWithA:") because of the standard name-translation rules.

thank's, I think I understand

but to construct the selector, I just have the name of the controller (as a String) and the name of the metod (as a String)

and the compiler rejects

nameofController:String = "myController)"

nameOfMethod:String = "aMethod"

#selector (nameOfController+"."+nameOfMethod+"(:)")

but to construct the selector, I just have the name of the controller (as a String) and the name of the metod (as a String)

Fundamentally selectors don’t include the class name. You can supply a class name when using the

#selector
syntax, but that’s just a convenience. The resulting selector is based entirely on the method signature.

Consider this:

import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBAction func testAction(_ sender: Any) {
NSLog("AppDelegate.testAction(_:)")
let sel = #selector(AppDelegate.testAction(_:))
NSLog("%@", "\(sel)")
}
}

which prints:

2017-08-31 09:53:15.201671+0100 TestApp[380:5209111] testAction:

If you want to create a selector from a string, you just pass in the method name. For example:

let str = "testAction:"
let sel = Selector(str)
NSLog("%@", "\(sel)")

prints exactly the same result as the previous snippet.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

No, it doesn't work like that. You have two choices:


1. You can construct a selector from a raw String. In this case you must make sure that the selector is correctly formed according to Obj-C rules.


For example, you can write Selector ("aMethod") or #selector ("aMethod").


2. You can construct a selector from the Swift name of a function. In this case, the name must be qualified enough for the compiler to know which function you mean. In general, that means you must say what class the function belongs to, and say what the parameter names are.


For example, you can write #selector(aController.aMethod).


But you can't mix the two.


The complication is that a selector is derived from the Obj-C name of a method, which is not the same as the Swift name — it doesn't have the same syntax, and there's a conversion rule that the Swift compiler follows, which isn't obvious.


Where are you getting the name the method (as a String) from? In general, just knowing the Swift name isn't enough to be able to construct the correct selector.

The problem is that I want to subclass a Combobox Control whose content is unknown and is loaded at run time by a webserver (and I also hav pther reasons to subclass controls)


I dont want to user outlets like in Eskimo solution, because I will have more than hundred window controllers in the program, with zoen of controls each, and outlets are too heavy to implemet.


So lets say that in a window Controller, I have 2 combobox controls with identifier "filter1" for the first and "filter2" for the second

By convention, I say that if a method exists to load the controls, his name will be load<Identifier of combobox>


for instance:

class myWindow: NSWindowController {

............

@objc func loadMyfilter1 (ctrl: NSCombobox) {

... do something ...

}

}


in this example, there is a method to load filter1, and no method to load filter2 (why not?)


that's why I dont't know in advance ythe name of ythe method


Now, If I iterate (in an other class, to make it harder) on the list of controls, I have somewhere in that other class:


func test (controller: NSWindowController, ctrl: NSControl) {

if ctrl is NSCombobox {

let methodName: String = "load"+ctrl.identifier.capitalized

let aMethod = Slector(methodName) // or as you say let aMethod = #selector(methodName)

if controller.responds (to: aMethod) {

controller.perform (aMethod, with: ctrl)

}

}

}


this doe'snt work because loadMyfilter1 needs a parameter

if loadMyfilter1 is declared

class myWindow: NSWindowController {

............

@objc func loadMyfilter1 () {

... do something ...

}

}


it works

OK, got it.


To make this work, you need to know the Obj-C method name (i.e. selector string) corresponding to your Swift method name. There are two ways to approach this:


1. You can figure out how Swift does it. For example, in the following 4 methods:


@objc func someMethod1 ()

@objc func someMethod2 (_ parameter1: Int)

@objc func someMethod3 (_ parameter1: Int, parameter2: Int)

@objc func someMethod4 (parameter1: Int, parameter2: Int)


the Obj-C names are, respectively:


someMethod1

someMethod2:

someMethod3:parameter2:

someMethod4WithParameter1:parameter2:

2. You can specify the Obj-C name in the Swift declaration. For example:

@objc(loadMyfilter1:) func anyMethodNameYouWant (anyParameterKeywordYouWant: NSCombobox)

Note that there is one ":" because the method has one parameter. Then you can construct the string for the selector as:

let methodName = "load" + ctrl.identifier.capitalized + ":"

and use either Selector(methodName) or #selector(methodName) to get the selector itself.

thank you


the seletor is well constructed and it works

you are right, it wa a problem of signature

how do I make a selector in swift for a method with parameters
 
 
Q