如何用RxSwift处理收藏按钮的状态与API请求逻辑
解决方案
核心问题是你的代码无法区分isFavorite状态变化是来自网络数据更新还是用户点击操作,导致所有状态变化都触发了API请求。要解决这个问题,需要把两种状态来源分开处理,只在用户点击时发起API调用。
重构ViewModel逻辑
首先在ViewModel里拆分状态流,分别处理网络初始状态和点击操作:
// in viewmodel // 来自网络的初始/更新收藏状态 private let initialFavorite = selectedVideo .map { $0.isFavorite ?? false } .distinctUntilChanged() // 用户点击导致的状态切换,基于initialFavorite的最新值 private let tapToggleFavorite = onTapFavorite .withLatestFrom(initialFavorite) { _, currentFavorite in !currentFavorite } // 合并两个流,得到最终显示的isFavorite状态:网络更新会覆盖点击状态,点击会切换当前状态 let isFavorite = Observable.merge( initialFavorite, tapToggleFavorite ) .distinctUntilChanged() // 专门用于触发API请求的流:只有点击操作才会发送信号,携带当前视频和目标收藏状态 let favoriteAction = onTapFavorite .withLatestFrom(Observable.combineLatest(selectedVideo, initialFavorite)) { _, tuple in (video: tuple.0, targetFavorite: !tuple.1) }
订阅API请求
现在只需要订阅专门的favoriteAction流,而不是isFavorite的所有变化:
// when subscribe viewModel.favoriteAction .flatMapLatest { (video, targetFavorite) in if targetFavorite { return APIService.favoriteVideo(videoId: video.videoId) } else { return APIService.unfavoriteVideo(videoId: video.videoId) } } .subscribe(onNext: { _ in // 请求成功后的处理,比如更新本地状态或提示用户 }, onError: { error in // 处理请求失败的情况,比如恢复状态 }) .disposed(by: disposeBag)
逻辑说明
initialFavorite:纯网络来源的收藏状态,当视频切换或后端数据更新时自动更新,不会触发API。tapToggleFavorite:仅由用户点击触发,基于当前网络状态切换收藏值,用于更新UI显示。isFavorite:合并两个流,保证UI能同时响应网络更新和点击操作。favoriteAction:专门的API触发流,只有点击时才会执行,携带当前视频和目标状态,避免网络更新误触发请求。
内容的提问来源于stack exchange,提问作者Terriermon
相关产品推荐
相关产品推荐

