A Swift package for building macOS menu bar entirely in code — no XIB or storyboard required.
- macOS 15+
- Swift 6
Add the package via Swift Package Manager:
.package(url: "...", from: "1.0.0")Then add MainMenuTemplate to your target's dependencies.
Normally a Cocoa app loads its menu bar from MainMenu.xib (or storyboard). Follow these steps to build the menu entirely in code:
- Remove MainMenu.xib from the project
- Delete the
NSMainNibFilekey from Info.plist (build setting:INFOPLIST_KEY_NSMainNibFile) - Mark your AppDelegate with
@mainand implementstatic func main(), callingrunAppDelegate(_:)inside it — this creates theNSApplicationinstance, sets the delegate, and starts the run loop - Implement
@objc func setupMainMenu()—runAppDelegate(_:)automatically calls it atwillFinishLaunchingtiming via dynamic dispatch
import Cocoa
import MainMenuTemplate
@main
class AppDelegate: NSObject, NSApplicationDelegate {
static func main() {
runAppDelegate()
}
@objc func setupMainMenu() {
MainMenuBuilder.buildAndApply({[
MainMenuBuilder.applicationMenuTemplate(),
MainMenuBuilder.fileMenuTemplate(),
MainMenuBuilder.editMenuTemplate(),
MainMenuBuilder.viewMenuTemplate(),
MainMenuBuilder.windowMenuTemplate(),
MainMenuBuilder.helpMenuTemplate(),
]}, localizingWith: .main, tableName: "MainMenu")
}
}Build and apply the standard menu bar (Application / File / Edit / View / Window / Help) in one call:
@objc func setupMainMenu() {
MainMenuBuilder.apply(MainMenuBuilder.defaultMainMenuTemplate())
}apply(_:) assigns the menu to NSApp.mainMenu, registers system menus (Services, Window, Help), and applies localization.
Use buildAndApply(_:) to compose and apply a menu in a single step:
@objc func setupMainMenu() {
MainMenuBuilder.buildAndApply({
[
MainMenuBuilder.applicationMenuTemplate(),
MainMenuBuilder.fileMenuTemplate(),
MainMenuBuilder.editMenuTemplate(),
MainMenuBuilder.viewMenuTemplate(),
MainMenuBuilder.buildMenuItem(title: "Custom", {
[
.init(title: "Custom Item 1", action: nil),
.init(title: "Custom Item 2", action: nil),
]
}),
MainMenuBuilder.windowMenuTemplate(),
MainMenuBuilder.helpMenuTemplate(),
]
}, localizingWith: .main, tableName: "MainMenu")
}You can also use build(_:) and apply(_:) separately:
let menu = MainMenuBuilder.build {
[
MainMenuBuilder.applicationMenuTemplate(),
MainMenuBuilder.fileMenuTemplate(),
MainMenuBuilder.editMenuTemplate(),
MainMenuBuilder.viewMenuTemplate(),
MainMenuBuilder.windowMenuTemplate(),
MainMenuBuilder.helpMenuTemplate(),
]
}
MainMenuBuilder.apply(menu, localizingWith: .main, tableName: "MainMenu")Use buildMenuItem(title:icon:_:) to create custom top-level menus:
MainMenuBuilder.buildMenuItem(title: "Tools", {
[
.init(title: "Run Script", action: #selector(runScript), keyEquivalent: "r"),
.init(title: "Open Console", action: #selector(openConsole)),
]
})You can also insert items into an existing template using MenuItemBuilder and insertItems(_:afterSelector:):
let fileMenuItem = MainMenuBuilder.fileMenuTemplate()
fileMenuItem.submenu?.insertItems(.init({
[
.separator(),
.init(title: "Custom Action", action: #selector(customAction(_:))),
.separator(),
]
}), afterSelector: #selector(NSWindow.performClose(_:)))The package includes English and Japanese localizations via Resources/MainMenu.xcstrings.
To provide app-specific overrides, pass your bundle and table name:
MainMenuBuilder.apply(menu, localizingWith: .main, tableName: "MainMenu")The localizationMode parameter controls how the user bundle is applied:
| Mode | Behavior |
|---|---|
.override (default) |
User bundle takes priority; keys not found fall back to the package's built-in translations |
.replace |
User bundle is used exclusively; keys not found display the original English key as-is |
// Patch mode — supply only what differs from the built-in translations (default)
MainMenuBuilder.apply(menu, localizingWith: .main, localizationMode: .override)
// Replace mode — the user bundle is the sole source of translations
MainMenuBuilder.apply(menu, localizingWith: .main, localizationMode: .replace)| Method | Menu |
|---|---|
applicationMenuTemplate() |
App menu (About, Settings, Services, Quit, …) |
fileMenuTemplate() |
File (New, Open, Save, Print, …) |
editMenuTemplate() |
Edit (Undo, Cut/Copy/Paste, Find, …) |
viewMenuTemplate() |
View (Toolbar, Sidebar, Full Screen) |
windowMenuTemplate() |
Window (Minimize, Zoom, Bring All to Front) |
helpMenuTemplate() |
Help |
formatMenu() |
Format — Font, Text (for text editing apps) |
findMenu() |
Find promoted to a top-level menu |
buildMenuItem(title:icon:_:) |
Custom menu with arbitrary items |
See LICENSE for details.