I have a menubar-only (Mac) SwiftUI app. The core of it is this:
// MyMainApp.swift
var body: some Scene {
MenuBarExtra("My App", image: "LoggedOutIcon") {
MenuBarView()
}
}
I'd like to dynamically update the image to "LoggedInIcon" whenever the user is logged in, and "LoggedOutIcon" whenever the user is logged out. That functionality is set in a (non-view) controller.
I figured the way to do this was to pass in my main controller to my app like this:
// MyMainApp.swift
@StateObject var myMainController = MyMainController()
Within that, set a published variable like this:
// MyMainController.swift
@Published var loggedIn: Bool = false
And then update the MenuBarExtra call to
// MyMainApp.swift
MenuBarExtra("My App", image: myMainController.loggedIn ? "LoggedInIcon" : "LoggedOutIcon")
The good news is: it works. The bad news is, that @StateObject var myMainController
line raises the following purple notification of doom:
Accessing StateObject's object without being installed on a View. This will create a new instance each time.
...That seems like something I should avoid.
In short: what is the best practice for updating a MenuBarExtra
icon in a SwiftUI app outside the scope of a view?
If the
loggedIn
property is toggled by a view further down in your hierarchy-- e.g. inMenuBarView
:Then you can use the
@AppStorage
property wrapper and inMyMainApp.Swift
:If you want it to be toggled in the
MyMainController
, you'd still use@AppStorage
but instead of having a@Binding
to it in a View, you'd do:If you're interested in why swift is showing you this error, there is a good discussion about it here