Search code examples
swiftmacosswift3nsmenunsmenuitem

Selector() with parameter in Swift + NSMenuItem


I'm currently trying to list all keys in a Keychain as NSMenuItems and when I click one, I want it to call a function with a String parameter BUT with my current code every key gets removed when I run my app not only the key I click on.

This is my current code:

NSApplicationMain

class AppDelegate: NSObject, NSApplicationDelegate {

   let menu = NSMenu()
   let internetKeychain = Keychain(server: "example.com", protocolType: .https, authenticationType: .htmlForm)

  func applicationDidFinishLaunching(_ aNotification: Notification) {
    for key in internetKeychain.allKeys() {
        menu.addItem(NSMenuItem(title: "🚮 \(key)", action: Selector(deleteKey(key: "\(key)")), keyEquivalent: ""));
    }

    if let button = statusItem.button {
        button.title = "🔑"
        button.target = self }
        statusItem.menu = menu
        NSApp.activate(ignoringOtherApps: true)
  }

  func deleteKey(key: String) -> String {
    do {
        try addInternetPasswordVC().internetKeychain.remove("\(key)")
        print("key: \(key) has been removed")
    } catch let error {
        print("error: \(error)") }
    refreshMenu()
    return key
  }

...
}

I suspect

  • Option 1: Selectors accept functions with parameters (or just in some extent)
  • Option 2: I made a little mistake in the function in the first or last line.

Solution

  • The signature of a target / action method takes either no parameter or passes the affected item (in this case the NSMenuItem instance) and I doubt that it can return anything.

    menu.addItem(NSMenuItem(title: "🚮 \(key)", action: #selector(deleteKey(_:)), keyEquivalent: ""));
    
    ...
    
    func deleteKey(_ sender: NSMenuItem) {
        do {
            let key = sender.title.substring(from: sender.title.range(of: " ")!.upperBound)
            try addInternetPasswordVC().internetKeychain.remove("\(key)")
            print("key: \(key) has been removed")
            refreshMenu()
        } catch let error {
            print("error: \(error)") 
        }
    }
    

    PS: To call refreshMenu() is only useful when the key is removed I guess.