Reputation: 2282
I'm looking to not show a window for a SwiftUI application on macOS. The app uses SwiftUI's application lifecycle and only runs in the status bar. Showing a window on start up is unnecessary. I'm unsure however how to get around the WindowGroup
. There's no such a thing as an EmptyScene
and putting an EmptyView
inside the WindowGroup
of course creates an empty window.
@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
I basically only need the app delegate. I'm guessing using the default AppKit lifecycle makes more sense, but if there is a way to use SwiftUI's lifecycle, I'd love to know.
Upvotes: 22
Views: 6466
Reputation: 9832
Since this a status bar menu application …
The app uses SwiftUI's application lifecycle and only runs in the status bar.
… use a MenuBarExtra
scene to render a persistent control in the system menu bar with a SwiftUI lifecycle. (macOS 13.0+)
@main
struct MyStatubarMenuApp: App {
var body: some Scene {
// -- no WindowGroup required --
// -- no AppDelegate needed --
MenuBarExtra {
Text("Hello Status Bar Menu!")
MyCustomSubmenu()
Divider()
Button("Quit") { NSApp.terminate(nil) }
} label: {
Image(systemName: "bolt.fill")
}
}
}
Notes:
Application is agent = YES
in Info.plist
for the app icon to not show on the dock.systemName:
SF Symbols will automatically toggle appearance when the System Settings…
> Appearance
is changed.@Environment(\.colorScheme)
does not currently update at the App/Scene levelUpvotes: 11
Reputation: 3422
var body: some Scene {
WindowGroup{
EmptyView()
.frame(width: 0, height: 0)
.hidden()
}
.windowResizability(.contentSize)
}
the windowResizability(_:) is available on macOS 13+
Upvotes: 0
Reputation: 1590
If you app has settings (who doesn't?), you can do like this:
@main
struct TheApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
Settings {
SettingsView()
}
}
}
Upvotes: 10
Reputation: 2156
You should be able to do something like this:
var body: some Scene {
WindowGroup {
ZStack {
EmptyView()
}
.hidden()
}
}
Upvotes: 6
Reputation: 251
In your AppDelegate, do the following in your applicationDidFinishLaunching:
func applicationDidFinishLaunching(_ notification: Notification) {
// Close main app window
if let window = NSApplication.shared.windows.first {
window.close()
}
// Code continues here...
}
For example:
class AppDelegate: NSObject, NSApplicationDelegate {
var popover = NSPopover.init()
var statusBar: StatusBarController?
func applicationDidFinishLaunching(_ notification: Notification) {
// Close main app window
if let window = NSApplication.shared.windows.first {
window.close()
}
// Create the SwiftUI view that provides the contents
let contentView = ContentView()
// Set the SwiftUI's ContentView to the Popover's ContentViewController
popover.contentSize = NSSize(width: 160, height: 160)
popover.contentViewController = NSHostingController(rootView: contentView)
// Create the Status Bar Item with the above Popover
statusBar = StatusBarController.init(popover)
}
}
Upvotes: 25