> You know, I should really stop now because this is way outside of my area of expertise, but I just can’t let it go
🙂
Well, it might very well be a Swift issue - and this thread started out as one and this is how I found this thread. The issue discussed is:
override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool
Not working anymore in Swift 4.2.
Before Swift 4.2 you have been required to override `validateMenuItem`, and call super (or not). With Swift 4.2 the `override` is not possible anymore and the code needs to be ported - somehow. So clearly, at least on the Swift side, Swift 4.0 NSViewController did implement `validateMenuItem` and one had to use `override`.
Your answer wrt the formal protocol is related and important information here, but only part of the issue.
If you want to spend the time, this is really easy to reproduce. In Xcode 10 create a new Cocoa AppKit Swift project. Go to the Build Settings and search for "swift language version", set it from 4.2 to 4.0. Just add something like this to the AppDelegate.swift:
class MyVC : NSViewController {
override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
return super.validateMenuItem(menuItem)
}
}
And play with it ;-) Also do what I did and set the version back up to 4.2 and experience what the original poster, and myself, experienced. The super implementation is gone. You are saying that the "I don’t think the OS version matters". That is probably right, but the SDK selected may matter, maybe the one used by 4.0 included that method (maybe an older ObjC SDK does too).
So now about the actual functionality of `validateMenuItem`. From my understanding, which may very well be wrong :-) So this selector is to override the behaviour to enable/disable menu items.
By default the menu item would just walk the responder chain and check whether there is an object responding to the action selector attached to the menu item. If yes, all good, enable, if no, disable.
But sometimes, checking the selector is not enough. For example even if the controller implements `delete:`, it makes no sense to enable the delete-menu-item if for example no item is selected in a tableview. This is when you override `validateMenuItem`. So instead of checking for a selector, the menu item enable/disable logic first checks for `validateMenuItem:`. If it is there, it lets it do the job to determine whether the object can handle it right now.
Now the problem is that if you implement `validateMenuItem:`, but there is no super, what to you do? You may want to override the behaviour for just a single action, and just have the default behaviour for everything else. In Swift 4 you would just call super and it worked (as far as I can tell my code worked just fine [e.g. CodeCows crashes on 10.14 right now for other reasons, will upload a fix soon]).
So unless there is some special Swift hackery in the AppKit shim, I think it is most plausible (and sound) that before the move to the formal protocol, AppKit included a default implementation like this:
@implementation NSObject(ValidateMenuItem)// or NSResponder maybe?
- (BOOL)validateMenuItem:(NSMenuItem *)_item {
return [self respondsToSelector:[_item action]];
}
@end
At some point that seems to have been removed in the 4.0...4.2 timeframe. That this is also gone in ObjC just supports my theory :-) I bet a beer that this category (or at least something for NSViewController) has been disabled if you compile against the newest SDK.
Regardless of all of that, I think my answer is what people affected by the original posters issue will have to do in Swift 4.2 (in addition to explicitly conforming to the `NSMenuItemValidation` protocol):
@objc func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
switch menuItem.action {
case #selector(delete(_:)): return selectedAccount != nil
default: return responds(to: menuItem.action) // instead of super
}
}
P.S.: I gave it a try w/ Xcode 9.2 on 10.12 and 10.10 as the deployment target, and ObjC. ObjC still can't call super. So it does seem to be Swift specific.