How to make a Cocoa application without a .xib file

In XCode, go to File > New > Project... and select “Cocoa Application”. Call it “Foo”, then uncheck all of the “Use Storyboards”, “Use Core Data”, and other nonsense. You get a project which, when you run it, displays a window with the title “Foo”. How does that window get there?

Turns out, even though you unchecked “Use Nonsense”, you still get a nonsense file: that MainMenu.xib. What is it, and how do we get rid of it? Cocoa applications begin with a call to NSApplicationMain. In C and Objective-C, you call NSApplicationMain from your main function. The call to NSApplicationMain never returns; instead, it sets up the UI event loop which runs until the program exits with the exit function.

It is the NSApplicationMain call which “loads the main nib file from the application’s main bundle”. (For “nib”, read “xib”: the “nib” format is an older syntax which was replaced with the XML-based “xib” format.)

It is unclear how NSApplicationMain finds the “main xib file” which it loads. One way is via the NSMainNibFile in your Info.plist file. However, if you remove this key in the plist file, Cocoa still finds your xib file. It will even find your xib file if you rename it Foo.xib. It is as if Cocoa falls back to a general search for files ending in .xib.

If, like me, you’re using Swift, it will also be unclear how NSApplicationMain is called. You will have seen the similarly named @NSApplicationMain annotation on your AppDelegate class. You can see it in the default AppDelegate.swift, which looks like:

import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!

The @NSApplicationMain annotation can be seen a bit like a macro. Roughly, it will create a main.swift file with these contents:

import AppKit
let app: NSApplication = NSApplication.shared()
let appDelegate = AppDelegate()  // Instantiates the class the @NSApplicationMain was attached to
app.delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

The main.swift file is special: it can contain top-level statements. It’s a bit like the body of the main function in C. Replace @NSApplicationMain with this main.swift, and then you can remove your MainMenu.xib file.