1024jp 1024jp - 1 year ago 149
Swift Question

Best practical way to validate NSTouchBar items

On AppKit, menu items and toolbar items have

respectively. However, by new touch bar items, there is no such convenience method to validate appropriate items at the right moment.

I'm now validating touch bar items every time when I change the related values and invoke a validation method in
(see the following sample code). But I feel it is not a good way because the related values must know there is a touch bar item depending on it.

var foo: Foo? {
didSet {
if #available(macOS 10.12.1, *), NSClassFromString("NSTouchBar") != nil {
self.validateTouchBarItem(identifier: .foo)

@available(macOS 10.12.1, *)
func validateTouchBarItem(identifier: NSTouchBarItemIdentifier) {

let item = self.touchBar?.item(forIdentifier: identifier),
let button = item.view as? NSButton
else { return }

switch identifier {
case NSTouchBarItemIdentifier.foo:
button.isEnabled = (self.foo != nil)

default: break

Another way I'm using is the Cocoa-binding and KVO, however, it doesn't always work well.

So, I'm curious whether there is any recommended or a defacto-standard way to validate touch bar items, especially containing NSButton and NSSegmentedControl. I wanna change not only the availability of items but sometimes also images or colors of them depending on the situation. How do you guys validate touch bar items?

Answer Source

I improved my touch bar validation system by myself and made the following protocol and extensions.

@available(macOS 10.12.1, *)
protocol TouchBarItemValidations: class {

    func validateTouchBarItem(_ item: NSTouchBarItem) -> Bool

@available(macOS 10.12.1, *)
extension NSTouchBarProvider {

    func validateTouchBarItems() {

        guard NSClassFromString("NSTouchBar") != nil else { return }  // run-time check

        guard let touchBar = self.touchBar else { return }

        // validate currently visible touch bar items
        for identifier in touchBar.itemIdentifiers {
            guard let item = touchBar.item(forIdentifier: identifier) as? NSCustomTouchBarItem else { continue }



@available(macOS 10.12.1, *)
extension NSCustomTouchBarItem: NSValidatedUserInterfaceItem {

    func validate() {

        // validate content control
        if let control = self.control,
            let action = control.action,
            let validator = NSApp.target(forAction: action, to: control.target, from: self)
            if let validator = validator as? TouchBarItemValidations {
                control.isEnabled = validator.validateTouchBarItem(self)

            } else if let validator = validator as? NSUserInterfaceValidations {
                control.isEnabled = (validator as AnyObject).validateUserInterfaceItem(self)

    // MARK: Validated User Interface Item Protocol

    public var action: Selector? {

        return self.control?.action

    public var tag: Int {

        return self.control?.tag ?? 0

    // MARK: Private Methods

    private var control: NSControl? {

        return self.view as? NSControl


It requires subclassing NSWindow that uses touchBar (or NSApplication's updateWindows() if you add touchBar to the application level), but except for that, nothing complex to add. You can now use validateTouchBarItem(_:) or normal validateUserInterfaceItem(_:) to validate your own touch bar items!

// MARK: Touch Bar Validation
class Window: NSWindow {

    override func update() {


        if #available(macOS 10.12.1, *) {
            for responder in sequence(first: self.firstResponder, next: { $0.nextResponder }) {


I suppose this works well at least for my requirements.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download