SwiftUI中@AppStorage变更后.onChange未触发问题排查
问题分析与解决
核心问题
你的实现存在两个关键错误,导致onChange无法触发:
ViewModel实例不共享
在ExecuteShortcutIntent的perform方法里,你新建了一个ShortcutsViewModel实例:let viewModel = ShortcutsViewModel() viewModel.wasExecuted = true这个实例和
ShortcutView里创建的viewModel是完全独立的两个对象。你修改的是临时实例的wasExecuted,而ShortcutView绑定的那个ViewModel的属性根本没变化,自然不会触发onChange。@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
相关产品推荐
相关产品推荐

