`NSStatusItem` created from `main()` doesn't show up on system status bar

1.1k Views Asked by At

I'm trying to make a simple macOS Cocoa application using NSStatusItem to create a clickable icon on the system status bar. However, when I launch my application, I get this warning and the icon doesn't show up:

2020-03-03 14:43:11.564 Mocha_bug_example[936:39572] CGSGetActiveMenuBarDrawingStyle((CGSConnectionID)[NSApp contextID], &sCachedMenuBarDrawingStyle) returned error 268435459 on line 46 in NSStatusBarMenuBarDrawingStyle _NSStatusBarGetCachedMenuBarDrawingStyle(void)

Here's a minimal reproducible example for my application:

#import <AppKit/AppKit.h>

NSStatusItem* statusItem;

int main (int argc, char* argv[]) {
        statusItem = [NSStatusBar.systemStatusBar statusItemWithLength: -1];
        statusItem.button.title = @"foobar";
        statusItem.visible = YES;

        [NSApplication.sharedApplication run];
        return 0;
}

I compiled and ran the example like this:

MacBook-Air-5:Mocha ericreed$ clang -o Mocha_bug_example -framework AppKit -fobjc-arc Mocha_bug_example.m
MacBook-Air-5:Mocha ericreed$ ./Mocha_bug_example
2020-03-03 14:43:11.564 Mocha_bug_example[936:39572] CGSGetActiveMenuBarDrawingStyle((CGSConnectionID)[NSApp contextID], &sCachedMenuBarDrawingStyle) returned error 268435459 on line 46 in NSStatusBarMenuBarDrawingStyle _NSStatusBarGetCachedMenuBarDrawingStyle(void)
[Application hung until I pressed Ctrl+C]
^C
MacBook-Air-5:Mocha ericreed$ 

Note: disabling automatic reference counting and adding [statusItem release]; after calling run as this similar question suggested made no visible difference.

3

There are 3 best solutions below

9
James Bucanek On BEST ANSWER

This is not the kind of thing you can do in main().

Except for extrememly unusual situations, you should never modify the main() that comes with the application template, and it must call NSApplicationMain():

int main(int argc, char *argv[])
{
    // start the application
    return NSApplicationMain(argc, (const char **) argv);
}

The Cocoa framework doesn't get initialized until you call NSApplicationMain() and is generally unusable until then.

This kind of setup should be done in applicationWillFinishLaunching or applicationDidFinishLaunching.

Update

The original poster is not using Xcode and is willing to brave the wilderness alone. ;)

This also implies that their application bundle will not have a main NIB file that would normally create and connect the application delegate object, main menu, and so forth.

There are intrepid individuals who have braved this territory and you can read about it in Creating a Cocoa application without NIB files.

2
apodidae On
#import <Cocoa/Cocoa.h>

int main(){
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
 NSApplication *application = [NSApplication sharedApplication];
 NSStatusItem* statusItem;
 statusItem = [NSStatusBar.systemStatusBar statusItemWithLength: -1];
 statusItem.button.title = @"foobar";
 statusItem.visible = YES;
 [application run];
 [pool drain];
 return 0;
}

Save file with the name 'statusBar_SO.m'

Compile from Terminal: clang statusBar_SO.m -framework Cocoa -o statusBar && ./statusBar

2
Andrew Barker On

This is how to add status bar item to command line app mac osx cocoa

Adapting apodidae's answer to Swift. Just put this in the main.swift file:

let app = NSApplication()
let statusItem = NSStatusBar.system.statusItem(withLength: -1)
statusItem.button!.title = "Hello, world!"
app.run()

I don't understand the finer details of the NSReleasePool as apodidae included, but it works for me without that.