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

SwiftUI中@AppStorage变更后.onChange未触发问题排查

问题分析与解决

核心问题

你的实现存在两个关键错误,导致onChange无法触发:

  1. ViewModel实例不共享
    在ExecuteShortcutIntent的perform方法里,你新建了一个ShortcutsViewModel实例:

    let viewModel = ShortcutsViewModel()
    viewModel.wasExecuted = true
    

    这个实例和ShortcutView里创建的viewModel是完全独立的两个对象。你修改的是临时实例的wasExecuted,而ShortcutView绑定的那个ViewModel的属性根本没变化,自然不会触发onChange。

  2. @AppStorage在ObservableObject中的更新通知缺失
    即使通过UserDefaults同步了shortcutWasExecuted的值,ShortcutsViewModel作为ObservableObject,默认情况下@AppStorage属性变化时不会自动发送objectWillChange通知,视图无法感知到属性更新。

修复方案

方案1:使用单例确保ViewModel实例唯一

修改ShortcutsViewModel为单例,让Intent和View共用同一个实例:

public class ShortcutsViewModel: ObservableObject {
    static let shared = ShortcutsViewModel()
    @AppStorage("shortcutWasExecuted") public var wasExecuted: Bool = false {
        didSet {
            objectWillChange.send() // 手动发送更新通知
        }
    }
    
    func executeShortcuts(shortcutId: String?) {
        // 你的执行逻辑
    }
}

然后在View和Intent中使用单例:

struct ShortcutView: View {    
    @ObservedObject var viewModel = ShortcutsViewModel.shared
    // ... 其余代码不变
}

struct ExecuteShortcutIntent: AppIntent {
    func perform() async throws -> some IntentResult {
        ShortcutsViewModel.shared.wasExecuted = true
        return .result()
    }
}

方案2:直接操作UserDefaults并监听变化

如果不想用单例,可以直接修改UserDefaults,同时让ViewModel监听该键的变化:

public class ShortcutsViewModel: ObservableObject {
    @Published var wasExecuted: Bool = UserDefaults.standard.bool(forKey: "shortcutWasExecuted")
    
    private var observer: NSObjectProtocol?
    
    init() {
        // 监听UserDefaults变化
        observer = NotificationCenter.default.addObserver(
            forName: UserDefaults.didChangeNotification,
            object: nil,
            queue: .main
        ) { [weak self] _ in
            guard let self = self else { return }
            let newValue = UserDefaults.standard.bool(forKey: "shortcutWasExecuted")
            if newValue != self.wasExecuted {
                self.wasExecuted = newValue
            }
        }
    }
    
    deinit {
        if let observer = observer {
            NotificationCenter.default.removeObserver(observer)
        }
    }
    
    func executeShortcuts(shortcutId: String?) {
        // 你的执行逻辑
    }
}

Intent中直接修改UserDefaults:

struct ExecuteShortcutIntent: AppIntent {
    func perform() async throws -> some IntentResult {
        UserDefaults.standard.set(true, forKey: "shortcutWasExecuted")
        return .result()
    }
}

额外提示

  • 避免在View中直接初始化ViewModel(@ObservedObject var viewModel: ShortcutsViewModel = ShortcutsViewModel()),建议通过父视图注入,这样更利于测试和实例共享。
  • 如果使用@AppStorage在ObservableObject中,一定要手动添加didSet发送objectWillChange通知,否则视图无法感知到属性变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 01:50:17