Search code examples
objective-cxcodecocoamacos-sierranstouchbar

Adding NSTouchBar support after main window has been created


I'm trying to add support for exposing NSTouchBar buttons via a plugin to an application that I cannot otherwise modify. The plugin, a shared library, is loaded at runtime after the main window has been created. I've created an AppDelegate as follows:

@interface AppDelegate : NSResponder <NSTouchBarDelegate>
@end

With an @implmentation that implements the makeTouchBar and touchBar functions:

- (NSTouchBar *) makeTouchBar
- (nullable NSTouchBarItem *) touchBar:(NSTouchBar *)touchBar
                       makeItemForIdentifier: (NSTouchBarItemIdentifier)identifier

I finally try to inject it into the application, I have the following in the onload function that's called in the dynamic library when it's loaded into the application.

NSWindow *w = [NSApp mainWindow];

AppDelegate *a = [[AppDelegate alloc] init];
a.nextResponder = w.nextResponder;
w.nextResponder = a;

// Re-setting FirstResponder will trigger AppDelegate::makeTouchBar
// that was attached above.
NSResponder *n = w.firstResponder;
[w makeFirstResponder: nil];
[w makeFirstResponder: n];

however... AppDelegate::touchBar will never be called, thus the touchbar will never be populated with my test buttons.

If I create a new project in Xcode, and use the exact same AppDelegate implementation (copy-paste of the same @interface and @implementation), I get functional buttons that are both shown and responds to press events, so it's the injection part that seems to be broken. (In Xcode everything is hooked up via MainMenu.xib I guess)

Update: The main problem was that the application was compiled in a way that prevented TouchBar to work. Perhaps built on an older version of Mac OS.


Solution

  • If this object really should act as the application delegate (as the name suggests) -- rather than inserting it in the main window's responder chain, it would be better to actually set it as the NSApp's delegate. The set delegate's touchBar is also used when building the touch bar (see NSTouchBar Discovery and the Responder Chain).

    If that doesn't work for your plugin (maybe the application already has a specific app delegate it needs), you could insert it into NSApp's responder chain instead of the mainWindow's.

    Both of these also have the benefit of making the touch bar be associated at the application level rather than window. So if the main/key window changes, the provided touch bar will always be used.