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

UserDefaults键路径Publisher仅触发首次值,SwiftUI接收却正常?

UserDefaults扩展属性订阅异常问题

问题场景

给UserDefaults扩展Objective-C兼容的动态属性:

@objc dynamic var someProp: String { string(forKey: "someProp") }

用常规方式订阅该属性的发布者,仅能获取初始值,后续更新无法触发回调:

private let sub = UserDefaults.standard.publisher(for: \.someProp).sink { print($0) }

但在SwiftUI中通过.onReceive观察同一个发布者,却能正常接收所有后续更新:

.onReceive(pub) { value in
    print("received", value)
}

最小复现示例

public extension UserDefaults {
    @objc dynamic var value1: Int {
        integer(forKey: "string1")
    }
}

struct ContentView: View {
    @StateObject var vm = ViewModel()

    private let pub = UserDefaults.standard.publisher(for: \.value1)

    var body: some View {
        Button("Add") {
            var value = UserDefaults.standard.value(forKey: "value1") as? Int ?? 0
            value += 1
            debugPrint("SET", value)
            UserDefaults.standard.set(value, forKey: "value1")
        }
        .onReceive(pub) { value in
            debugPrint("UI", value)
        }
    }

    class ViewModel: ObservableObject {
        private let sub = UserDefaults.standard.publisher(for: \.value1).sink {
            debugPrint("SUB", $0)
        }
    }
}

原因分析

  1. KVO触发条件不满足:
    你扩展的属性只有getter方法,没有对应的setter。当调用UserDefaults.set(_:forKey:)时,是直接操作UserDefaults的底层存储,并没有触发扩展属性的setter。而publisher(for:)底层依赖KVO机制,KVO仅在属性的setter被调用时才会发送通知,所以常规订阅拿不到后续更新。

  2. SwiftUI的特殊处理:
    SwiftUI视图在生命周期内会自动刷新,.onReceive会在每次视图刷新时重新建立订阅,因此能获取到最新值;而ViewModel中的订阅是一次性创建的,初始订阅后没有收到KVO通知,就不会再触发回调。

解决办法

给扩展的属性添加完整的getter和setter,让值的修改通过属性的setter完成,以此触发KVO:

public extension UserDefaults {
    @objc dynamic var value1: Int {
        get { integer(forKey: "value1") }
        set { set(newValue, forKey: "value1") }
    }
}

之后修改值时直接操作该属性:

UserDefaults.standard.value1 += 1

这样无论是ViewModel中的sink订阅,还是SwiftUI的.onReceive,都能正常接收所有更新通知。

另外注意:原复现代码中属性getter使用了错误的Key("string1"),需改为与属性名一致的"value1",避免读取数据错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:01:09