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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 16:35:01