SwiftUI与Firebase:能否通过@Published向Firebase Database收发单个值实现跨设备计数同步?
核心结论
可以实现跨设备近实时计数同步,但不能直接靠@Published修饰符完成和Firebase实时数据库的收发通信。它只是SwiftUI提供的本地属性包装器,仅负责在属性值变更时通知观察该对象的视图刷新UI,本身不会发起任何网络请求。要达到你要的同步效果,需要在自己的ObservableObject类内部,手动建立@Published属性和Firebase节点的双向绑定逻辑。
具体实现步骤
1. 提前配置好权限路径
因为已经完成Firebase Auth的接入,直接给每个登录用户分配独立的计数存储路径,避免多用户计数互相干扰,推荐路径结构为:
- 根节点下的
users/{当前用户uid}/count,专门存对应用户的计数值 - 数据库规则设置为仅认证用户可读写自己的对应节点,不开放公共读写权限。
2. 改造原有的CurrentCount类
把原来纯本地逻辑的类,加上Firebase实时监听、双向同步的逻辑,注意避免本地值变更触发云端写、云端值变更回传又触发本地值修改的无限循环问题。改造后的参考代码如下:
import FirebaseDatabase import FirebaseAuth class CurrentCount: ObservableObject { @Published var count = 0 private let rootRef = Database.database().reference() private var userCountRef: DatabaseReference? private var valueObserver: DatabaseHandle? init() { // 监听登录状态变化,用户登录后自动绑定对应的数据节点 Auth.auth().addStateDidChangeListener { [weak self] _, user in guard let self = self else { return } // 切换登录状态时先移除旧的数据库监听,避免内存泄漏 if let observer = self.valueObserver, let oldRef = self.userCountRef { oldRef.removeObserver(withHandle: observer) } guard let uid = user?.uid else { // 未登录状态下重置本地计数 self.count = 0 self.userCountRef = nil return } // 定位到当前用户专属的计数存储节点 self.userCountRef = self.rootRef.child("users").child(uid).child("count") // 监听云端的计数变化,实时同步到本地@Published变量,触发UI刷新 self.valueObserver = self.userCountRef?.observe(.value) { [weak self] snapshot in guard let self = self else { return } let cloudCount = snapshot.value as? Int ?? 0 // 仅当云端值和本地值不一致时才更新,打破同步循环 if cloudCount != self.count { self.count = cloudCount } } } } deinit { // 类销毁时移除所有数据库监听 if let observer = valueObserver, let ref = userCountRef { ref.removeObserver(withHandle: observer) } } // 封装统一的计数更新入口,替代直接修改@Published属性的逻辑 func updateCount(_ newValue: Int) { count = newValue // 将最新计数同步写入Firebase对应节点 userCountRef?.setValue(newValue) } }
3. 调整视图层的调用逻辑
原来代码中所有直接修改UserCount.count的动作,全部替换为调用上面封装的updateCount方法,否则直接修改本地属性不会触发云端同步:
- 减号按钮动作:将
self.UserCount.count -= 1改为self.UserCount.updateCount(self.UserCount.count - 1) - 加号按钮动作:将
self.UserCount.count += 1改为self.UserCount.updateCount(self.UserCount.count + 1) - 长按清零动作:将
self.UserCount.count = 0改为self.UserCount.updateCount(0)
常见注意事项
- 不要误以为@Published会自动完成网络请求,它的作用域仅在本地UI层,所有网络逻辑必须手动在ObservableObject内部实现。
- 这种基于Firebase实时数据库
observe方法的同步,正常网络下延迟在百毫秒级,完全满足tally计数器的近实时跨设备更新需求。 - 基础版本无需额外做离线缓存,Firebase SDK本身会自动处理离线状态下的写入排队,网络恢复后自动同步到云端。
内容的提问来源于stack exchange,提问作者Braeden La Marr
相关产品推荐
相关产品推荐

