Create a mac menu bar app in SwiftUI with MenuBarExtra
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.
How to make a mac menu bar app in SwiftUI
We need to do three things to create a mac menu app.
You can easily support sarunw.com by checking out this sponsor.
Offline Transcription: Fast, privacy-focus way to transcribe audio, video, and podcast files. No data leaves your Mac.
Create a macOS project
Let's start by creating a new macOS project.
Select File > New > Project... then select App under macOS platform.
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.
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 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.
Now, it is time to clean up unused code. We need to do two things.
Remove the main window
To remove the main window app, we remove WindowGroup
from <ProjectName>App.swift
.
@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.
Offline Transcription: Fast, privacy-focus way to transcribe audio, video, and podcast files. No data leaves your Mac.
Remove the dock icon
Even though we remove the main window scene, the app icon still shows in the Dock or the application switcher.
It is common for the menu bar app not to show that.
To remove the Dock icon.
- open Info.plist or open up the Info tab under your app target.
- Then add a new key "Application is agent (UIElement)" (
LSUIElement
flag), and sets the value toYES
.
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 ShareSwiftUI 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.
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.