SwiftUI中如何更新Toggle开关并将其状态同步到Firestore?
SwiftUI Toggle 与 Firestore 收藏状态同步方案
问题核心
当前依赖UserDefaults存储收藏状态,无法和Firestore实时同步,导致Toggle按钮状态在多设备切换、App重启后可能出现不一致。需要将收藏状态的数据源切换为Firestore实时监听,同时保留本地缓存优化体验。
改造后的ViewModel实现
import FirebaseFirestore import FirebaseFirestoreSwift class FavoriteViewModel: ObservableObject { @Published var favoriteItem: Set<String> = [] private let db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? private let userDefaultsKey = "favoriteItem" init() { // 先加载本地缓存作为初始值,避免网络加载时UI空白 loadLocalFavorites() // 启动Firestore数据监听,实时同步收藏状态 startFirestoreListener() } deinit { // 销毁监听,防止内存泄漏 listenerRegistration?.remove() } private func loadLocalFavorites() { if let storedFavorites = UserDefaults.standard.array(forKey: userDefaultsKey) as? [String] { favoriteItem = Set(storedFavorites) } } private func startFirestoreListener() { // 替换为你的用户身份标识逻辑,比如当前登录用户ID guard let userId = Auth.auth().currentUser?.uid else { return } listenerRegistration = db.collection("users").document(userId).collection("favorites") .addSnapshotListener { [weak self] querySnapshot, error in guard let documents = querySnapshot?.documents else { print("获取收藏数据失败: \(error?.localizedDescription ?? "未知错误")") return } // 从Firestore解析收藏项ID let favoriteIds = documents.compactMap { $0.documentID } self?.favoriteItem = Set(favoriteIds) // 更新本地缓存 UserDefaults.standard.set(favoriteIds, forKey: self?.userDefaultsKey ?? "") } } func isUserFavorite(itemId: String) -> Bool { favoriteItem.contains(itemId) } func toggleFavorite(itemId: String) { guard let userId = Auth.auth().currentUser?.uid else { return } let favoriteRef = db.collection("users").document(userId).collection("favorites").document(itemId) if isUserFavorite(itemId: itemId) { // 移除收藏 favoriteRef.delete { [weak self] error in if let error = error { print("移除收藏失败: \(error.localizedDescription)") // 操作失败时回滚本地状态,保证UI一致性 self?.favoriteItem.insert(itemId) } } } else { // 添加收藏,可按需存储额外字段(如添加时间) favoriteRef.setData(["addedAt": Timestamp()]) { [weak self] error in if let error = error { print("添加收藏失败: \(error.localizedDescription)") // 操作失败时回滚本地状态 self?.favoriteItem.remove(itemId) } } } } }
View层调整
无需大幅修改,@Published变量会自动触发UI更新,保证Toggle图标实时同步:
private var favoriteButton: some View { Button { viewModel.toggleFavorite(itemId: itemId) } label: { Image(systemName: viewModel.isUserFavorite(itemId: itemId) ? "bookmark.fill" : "bookmark") .foregroundColor(.orange) // 可选:用颜色强化状态区分 } }
关键优化点
- 实时同步:通过Firestore的
addSnapshotListener实现跨设备、跨会话的收藏状态实时同步 - 本地缓存兜底:初始化先加载UserDefaults缓存,避免网络加载时UI无响应
- 错误回滚:Firestore操作失败时回滚本地状态,保证UI与实际数据一致
- 内存管理:ViewModel销毁时移除监听,避免内存泄漏
内容的提问来源于stack exchange,提问作者Ali Almahoozi
相关产品推荐
相关产品推荐

