swiftmacosunrecognized-selector

Unrecognized selector on OS X 10.11


I'm using a custom item with menu in the system Status Bar for controlling some functions in my app. Here is my code:

import Foundation

class StatusBarMenuController {
    var statusItem: NSStatusItem

    init() {
        self.statusItem = NSStatusBar.system().statusItem(withLength: NSSquareStatusItemLength)

        statusItem.image = NSImage(named: "StatusBarButtonImage")

        let menu = NSMenu()            
        let isListeningMenuItem = NSMenuItem(title: "Listening", action: #selector(StatusBarMenuController.isListeningAction(_:)), keyEquivalent: "")

        isListeningMenuItem.isAlternate = true
        isListeningMenuItem.target = self
        isListeningMenuItem.state = NSOnState

        menu.addItem(isListeningMenuItem)

        statusItem.menu = menu
    }

    @objc func isListeningAction(_ item: NSMenuItem) {            
        if (item.state == NSOffState) {
            item.state = NSOnState
            // Handle switch-on action...
        }
        else {
            item.state = NSOffState
            // Handle switch-off action...
        }
    }
}

This class is instantiated in applicationDidFinishLaunching method of AppDelegate.

All works fine on the latest version of macOS (10.12) - I tried it on multiple computers, but when try to start the app on a machine with older version of os, e.g. OS X 10.11, it instantly crashes.

Crash details:

Application Specific Information: Unrecognized selector -[MyAppName.StatusBarMenuController methodForSelector:]

abort() called

Any ideas why is this happening?


Solution

  • Deriving from NSObject solved this issue:

    import Foundation
    
    class StatusBarMenuController: NSObject {
        var statusItem: NSStatusItem
    
        override init() {
            self.statusItem = NSStatusBar.system().statusItem(withLength: NSSquareStatusItemLength)
    
            super.init()
    
            statusItem.image = NSImage(named: "StatusBarButtonImage")
    
            let menu = NSMenu()            
            let isListeningMenuItem = NSMenuItem(title: "Listening", action: #selector(StatusBarMenuController.isListeningAction(_:)), keyEquivalent: "")
    
            isListeningMenuItem.isAlternate = true
            isListeningMenuItem.target = self
            isListeningMenuItem.state = NSOnState
    
            menu.addItem(isListeningMenuItem)
    
            statusItem.menu = menu
        }
    
        @objc func isListeningAction(_ item: NSMenuItem) {            
            if (item.state == NSOffState) {
                item.state = NSOnState
                // Handle switch-on action...
            }
            else {
                item.state = NSOffState
                // Handle switch-off action...
            }
        }
    }
    

    It's a very strange behavior, because in other parts of my app I'm using selectors with NotificationCenter in not NSObject-derived classes and it works, e.g.:

    class StatusBarMenuController {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleMyNotification),
            name: NSNotification.Name(rawValue: myNotification),
            object: nil
        )
    
        @objc func handleMyNotifiction(_ notification: Notification) {
            // ...
        } 
    }