Dictionary Index forkey as Int ?

Iterating over a Dictionary where Key is String and Value is Selector using ...

for (b,a) in buttons {
     let index = buttons.index(forKey: b)
}

I want to get an Int from the Index ? from Dictionary<String,Selector>.Index?

Accepted Reply

You have no need to use `enumerate()` or get an Int from the Index, your `forEach` code can be re-written in something like this:

        buttons.forEach {title, action in
            let button = alert.addButton(withTitle: title)
            button.action = action
        }


But, when you set up NSAlert with buttons, you may want to add buttons in a specific order.

You may need to remember that Dicitonary's order is not predictable.


Try executing this code repeatedly in the Playground:

let dict = ["Browse":"action01","Quit":"action02"]
dict.forEach {title, action in
    print(title, action)
}

Sometimes it outputs...

Browse action01

Quit action02


But sometimes...

Quit action02

Browse action01


You should better change the header of the method to use Array of tuples, not Dictionary.

static func showMessage(_ message: String,_ infoline: String, buttons: [(String,Selector)]) {


And about your main concern getting "unrecognized selector sent to instance".


Have you set the target of the button? You may need it to include in the tuple:

    static func showMessage(_ message: String,_ infoline: String, buttons: [(String,AnyObject,Selector)]) { //<-
        let alert = NSAlert()
        //...
        buttons.forEach {title, target, action in
            let button = alert.addButton(withTitle: title)
            button.action = action
            button.target = target //<-
        }
        //...
    }

Replies

Your post is really cryptic. You should take some time to explain a bit more the context. Help others to help you 😉


how is buttons defined ?

What would this Int contain ?

Thanks, will explain ...


It's an NSAlert in a func that has sent the Dictionary array let buttons = ["Browse":action,"Quit":action]

and in a for loop for (b,a) in buttons to iterate the Dictionary and want to add the buttons alert.addButton(withTitle: b) and add the actionalert.buttons[0].actionbut need an index (Int) for the alert's button array.

OK. I understand a little more, but not everything.


You create the alert and then want to add button. How do you select the buttons to dd (ignoring the order at this time) ?

Then how do you decide which should be first button, which second …


If you decide what will be the title of each (button 0, button 1…), then could you check which button in the buttons matches with this title ? (normally, 2 buttons should not have the same title).

Take care to localization issues.


Or may be I totally miss the point.

Last question: what is your intent doing so (dynamically selecting the buttons) ? May be explaining this would help further understand.

I'm counting on the order of the Array being the order of the Buttons. Trying to make the Alert's properties variable, but maybe I'm going about this in the wrong way ?


I've managed to get the results I need using ...


buttons.enumerated().forEach({
    let key = $0.element.key
    let value = $0.element.value
    let index = $0.offset
    alert.addButton(withTitle: key)
    alert.buttons[index].action = value
})


However now have a problem with the Selector, I'm getting "unrecognized selector sent to instance". The way I'm setting it and passing ...


let action = #selector(operation.runThis(_:))

static func showMessage(_ message: String,_ infoline: String, buttons: [String:Selector])

You have no need to use `enumerate()` or get an Int from the Index, your `forEach` code can be re-written in something like this:

        buttons.forEach {title, action in
            let button = alert.addButton(withTitle: title)
            button.action = action
        }


But, when you set up NSAlert with buttons, you may want to add buttons in a specific order.

You may need to remember that Dicitonary's order is not predictable.


Try executing this code repeatedly in the Playground:

let dict = ["Browse":"action01","Quit":"action02"]
dict.forEach {title, action in
    print(title, action)
}

Sometimes it outputs...

Browse action01

Quit action02


But sometimes...

Quit action02

Browse action01


You should better change the header of the method to use Array of tuples, not Dictionary.

static func showMessage(_ message: String,_ infoline: String, buttons: [(String,Selector)]) {


And about your main concern getting "unrecognized selector sent to instance".


Have you set the target of the button? You may need it to include in the tuple:

    static func showMessage(_ message: String,_ infoline: String, buttons: [(String,AnyObject,Selector)]) { //<-
        let alert = NSAlert()
        //...
        buttons.forEach {title, target, action in
            let button = alert.addButton(withTitle: title)
            button.action = action
            button.target = target //<-
        }
        //...
    }

Did not notice that !, the Tuple works perfect thanks. Using operation.runThis(_:) only works as operation.runThis otherwise I get the selector error, really wanting a way to pass a parameter to the function though. Also unclear but is "target" the object that has the function ?


I know this has gotten a little off topic, sorry.

I know this has gotten a little off topic, sorry.


Seems very little. But it may be better for you to start a new thread, with your latest code including relevant parts such as what is `operator` and how `runThis` is defined, as well as what sort of parameter you want to pass.