cocoansstatusitemnsapplicationnsstatusbar

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


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.


Solution

  • 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.