I’m writing a MenuBarExtra which needs to know the current application. I have some code which nearly does the job:
import SwiftUI
@main
struct XBApp: App {
@State var bundleID: String = ""
var body: some Scene {
MenuBarExtra("Something", systemImage: "questionmark.bubble") {
VStack {
XBContent(bundleID: bundleID)
.padding(0)
}
.onAppear {
bundleID = NSWorkspace.shared.frontmostApplication!.bundleIdentifier!
print("\(#fileID):\(#line) - \(bundleID)")
}
}
.menuBarExtraStyle(.window)
}
}
struct XBContent: View {
var bundleID: String = ""
@State var data: String = ""
init(bundleID: String) {
self.bundleID = bundleID // store received value
}
var body: some View {
VStack {
Text(bundleID) // current value
Text(data) // previous value
}
.onAppear {
data = bundleID // copy value
print("\(#fileID):\(#line) - \(bundleID) | \(data)")
}
}
}
The main App sets the current Bundle id in .onAppear
and this is then passed on to the XBContent
view. I think this part is doing its job correctly, but I’m open to correction.
The XBContent
view simply displays two versions of the passed bundle id:
init()
method stores the value in a property.onAppear
copies it into another variable for testing. In real life, it’s supposed to use this value to read some other data.VStack
displays the value from init()
and the copied value from onAppear
.Here’s the problem: the copied value from onAppear
is always one step behind the other. When the app is first launched, and the menu bar extra is opened, the first value is correct, while the second is the default empty string; this is fixed the next time. When I switch to another app and open the menu bar extra, the first value is the correct new bundle id, while the second is the old one, until I try it again and it’s OK.
I gather from this that onAppear
is not triggered when I thought it should be.
How can I get the second value to be the correct one?
Try this approach using @Binding var bundleID: String
. Note no need for the init(...)
in XBContent
, the view does it all for you. Tested on real device macOS 15.
@main
struct XBApp: App {
@State private var bundleID: String = "" // <--- here
var body: some Scene {
MenuBarExtra("Something", systemImage: "questionmark.bubble") {
VStack {
XBContent(bundleID: $bundleID) // <--- here
.padding(0)
}
.onAppear {
bundleID = NSWorkspace.shared.frontmostApplication!.bundleIdentifier!
print("\(#fileID):\(#line) - \(bundleID)")
}
}
.menuBarExtraStyle(.window)
}
}
struct XBContent: View {
@Binding var bundleID: String // <--- here
@State private var data: String = "" // <--- here
var body: some View {
VStack {
Text(bundleID)
Text(data)
}
.onAppear {
data = bundleID // copy value
print("\(#fileID):\(#line) - \(bundleID) | \(data)")
}
}
}
Since you have data = bundleID
, these two values will always be the same.