Create a mac menu bar app in SwiftUI with MenuBarExtra

⋅ 4 min read ⋅ macOS WWDC22

Table of Contents

In the previous post on How to make a macOS menu bar app, we learn how to make a mac menu bar app in AppKit.

This year in WWDC22 with iOS 16, Apple made it easy to create a menu bar app in SwiftUI.

I will replicate the toy app we created in How to make a macOS menu bar app using SwiftUI.

This is what the final result looks like.
It is an app that shows a number from 1 to 3, and you change it by tapping it and selecting the new number from the menu item.

The final result of our menu bar app.
The final result of our menu bar app.

How to make a mac menu bar app in SwiftUI

We need to do three things to create a mac menu app.

  1. Add a menu bar extra.
  2. Remove the main window.
  3. Remove the dock icon.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Create a macOS project

Let's start by creating a new macOS project.

Select File > New > Project... then select App under macOS platform.

New macOS project.
New macOS project.

Add a menu bar extra

A menu bar extra is the name for a menu bar that sits in the trailing end of the menu bar.

This is where we want to put a menu bar for a utility app.

We can easily add this with a new Scene called MenuBarExtra.

import SwiftUI

@main
struct swiftui_menu_barApp: App {
// 1
@State var currentNumber: String = "1"

var body: some Scene {
WindowGroup {
ContentView()
}
// 2
MenuBarExtra(currentNumber, systemImage: "\(currentNumber).circle") {
// 3
Button("One") {
currentNumber = "1"
}
Button("Two") {
currentNumber = "2"
}
Button("Three") {
currentNumber = "3"
}
}
}
}

1 We want our menu bar label to be changable based on user selection, so we create a state variable to keep the current number selection.
2 MenuBarExtra scene with a SF Symbols as a label. It can rotate between "1.circle", "2.circle", and "3.circle" based on user selection.
3 We populate our menu bar with three buttons which will render as a menu item. In the action, we change currentNumber which in-turn change the menu bar label.

With these few lines of code, we got a menu bar extra working.

A menu bar app with three items.
A menu bar app with three items.

Add a keyboard shortcut to each menu item

We can add a keyboard shortcut to a button using the .keyboardShortcut view modifier.

import SwiftUI

@main
struct swiftui_menu_barApp: App {
@State var currentNumber: String = "1"

var body: some Scene {
WindowGroup {
ContentView()
}
MenuBarExtra(currentNumber, systemImage: "\(currentNumber).circle") {
Button("One") {
currentNumber = "1"
}
.keyboardShortcut("1")
Button("Two") {
currentNumber = "2"
}
.keyboardShortcut("2")
Button("Three") {
currentNumber = "3"
}
.keyboardShortcut("3")
}
}
}

Keyboard shortcuts are usually composed of modifier keys, e.g., command, and another key, e.g., c, to form a shortcut.

.keyboardShortcut has a default .command modifier, so we don't need to specify that. We only need to specify a key that works with a command key.

The result shortcut key would be ⌘1, ⌘2, and ⌘3.

Add keyboard shortcuts with .keyboardShortcut modifier.
Add keyboard shortcuts with .keyboardShortcut modifier.

Add a way to quit

The final act is to add a way for users to quit your app.

This is just a matter of adding another button.

@main
struct swiftui_menu_barApp: App {
@State var currentNumber: String = "1"

var body: some Scene {
WindowGroup {
ContentView()
}
MenuBarExtra(currentNumber, systemImage: "\(currentNumber).circle") {
Button("One") {
currentNumber = "1"
}
.keyboardShortcut("1")
Button("Two") {
currentNumber = "2"
}
.keyboardShortcut("2")
Button("Three") {
currentNumber = "3"
}
.keyboardShortcut("3")
Divider()
Button("Quit") {
NSApplication.shared.terminate(nil)
}.keyboardShortcut("q")
}
}
}

We add a divider to separate the primary function from the quit menu. I use NSApplication.shared.terminate(nil) as a way to quit the app (not sure if there is a more SwiftUI way to doing this or not).

With this last change, we have a working menu bar app.

A menu bar app.
A menu bar app.

Now, it is time to clean up unused code. We need to do two things.

  1. Remove the main window.
  2. Remove the dock icon.

Remove the main window

To remove the main window app, we remove WindowGroup from <ProjectName>App.swift.

A boilerplate app.
A boilerplate app.
@main
struct swiftui_menu_barApp: App {
@State var currentNumber: String = "1"

var body: some Scene {
// Remove this
// WindowGroup {
// ContentView()
// }
MenuBarExtra(currentNumber, systemImage: "\(currentNumber).circle") {
Button("One") {
currentNumber = "1"
}
.keyboardShortcut("1")
Button("Two") {
currentNumber = "2"
}
.keyboardShortcut("2")
Button("Three") {
currentNumber = "3"
}
.keyboardShortcut("3")
Divider()
Button("Quit") {
NSApplication.shared.terminate(nil)
}.keyboardShortcut("q")
}
}
}

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Remove the dock icon

Even though we remove the main window scene, the app icon still shows in the Dock or the application switcher.

App icon still shows in the Dock.
App icon still shows in the Dock.

It is common for the menu bar app not to show that.

To remove the Dock icon.

  1. open Info.plist or open up the Info tab under your app target.
  2. Then add a new key "Application is agent (UIElement)" (LSUIElement flag), and sets the value to YES.
Set
Set "Application is agent (UIElement)" to Yes.

And that's all you need to do to create a menu bar app on macOS with SwiftUI.


Read more article about macOS, WWDC22, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
SwiftUI Grid

iOS 16 add a new Grid view to SwiftUI. A Grid view arranges child views in rows and columns. This table-like structure makes a layout that is hard to do in vertical and horizontal stacks become easier.

Next
How to create multiline TextField in SwiftUI

In iOS 16, we can create a multiple text field with new initializers and a little help from the .lineLimit(_:) modifier.

← Home