You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI MenuBarExtra获取Bundle Identifier数据滞后问题求助

问题:MenuBarExtra获取活跃应用Bundle ID时数据滞后

开发MenuBarExtra时,需要获取当前活跃应用的Bundle Identifier,但现有代码存在数据滞后问题:首次打开菜单时data为空;切换应用后打开菜单,data显示旧的Bundle ID,再次打开才更新。

当前代码如下:

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)")
        }
    }
}

问题核心是.onAppear触发时机不可靠:菜单的.onAppear和XBContent的.onAppear执行顺序不固定,且bundleID更新后,XBContent的.onAppear不会重新触发,导致data始终获取上一次的旧值。


解决方案

方法一:通过onChange监听数据变化,同步更新data

去掉依赖.onAppear的复制逻辑,改用onChange监听bundleID的更新,同时保留首次加载的初始化:

struct XBContent: View {
    let bundleID: String
    @State private var data: String = ""
    
    var body: some View {
        VStack {
            Text(bundleID)
            Text(data)
        }
        .onChange(of: bundleID) { newValue in
            // 这里可添加基于newValue的额外业务逻辑
            data = newValue
            print("\(#fileID):\(#line) - \(bundleID) | \(data)")
        }
        .onAppear {
            // 首次加载初始化data
            data = bundleID
            print("\(#fileID):\(#line) - \(bundleID) | \(data)")
        }
    }
}

方法二:实时监听应用激活事件,提前更新bundleID

通过监听系统通知,在应用切换时立即更新bundleID,避免等到菜单打开才获取数据:

@main
struct XBApp: App {
    @State private var bundleID: String = ""

    var body: some Scene {
        MenuBarExtra("Something", systemImage: "questionmark.bubble") {
            XBContent(bundleID: bundleID)
                .padding(0)
        }
        .menuBarExtraStyle(.window)
        // 监听应用激活事件,实时更新bundleID
        .onReceive(NotificationCenter.default.publisher(for: NSWorkspace.didActivateApplicationNotification)) { _ in
            if let app = NSWorkspace.shared.frontmostApplication {
                bundleID = app.bundleIdentifier ?? ""
            }
        }
        // 初始化时获取一次当前活跃应用的ID
        .onAppear {
            if let app = NSWorkspace.shared.frontmostApplication {
                bundleID = app.bundleIdentifier ?? ""
            }
        }
    }
}

关键注意点

  • 不要依赖.onAppear的执行顺序传递数据,SwiftUI视图生命周期事件顺序无严格保证。
  • 实时同步类数据优先用onChange监听变量变化,或通过绑定、ObservableObject管理状态。
  • 监听NSWorkspace.didActivateApplicationNotification能在应用切换时立即更新数据,无需等待菜单打开。

内容的提问来源于stack exchange,提问作者Manngo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 13:01:01