提问人:Terriermon 提问时间:7/7/2023 更新时间:7/8/2023 访问量:34
如何使用 Rxswift 处理程序收藏按钮
How to use the Rxswift handler the favorite button
问:
我有一个最喜欢的按钮,该按钮由网络数据更改并点击按钮。当 isSelected 通过点击按钮时,它需要请求收藏或不喜欢的 API,但当它被网络数据更改时,它不需要请求 api。
这是我的代码,当 isSelected 更改时,它总是请求 api。isSelected
// in viewmodel
isFavorite = selectedVideo
.map { $0.isFavorite ?? false }
.flatMapLatest({ favorite in
onTapFavorite.scan(favorite) { acc, _ in !acc }.startWith(favorite)
})
.distinctUntilChanged()
// when subscribe
Observable.combineLatest(viewModel.isFavorite, viewModel.selectedVideo)
.flatMapLatest({ (isFavorite, video) in
if isFavorite {
return APIService.favoriteVideo(videoId: video.videoId)
} else {
return APIService.unfavoriteVideo(videoId: video.videoId)
}
})
.subscribe(onNext: { _ in
}).disposed(by: disposeBag)
答:
1赞
Daniel T.
7/8/2023
#1
你有两个功能,所以你应该有两个可观察链......
服务器更新链如下所示:
func updateServer(selectedVideo: Observable<Video>, onTapFavorite: Observable<Void>) -> Observable<(videoId: Video.ID, isFavorite: Bool)> {
selectedVideo
.flatMapLatest { video in
onTapFavorite
.scan(video.isFavorite ?? false) { isFavorite, _ in !isFavorite }
.map { (videoId: video.id, isFavorite: $0) }
}
}
并且是这样绑定的:
updateServer(selectedVideo: selectedVideo, onTapFavorite: favoriteButton.rx.tap.asObservable())
.flatMapLatest {
$0.isFavorite ? APIService.favoriteVideo(videoId: $0.videoId) : APIService.unfavoriteVideo(videoId: $0.videoId)
}
.subscribe(onError: { error in
// handle error
})
.disposed(by: disposeBag)
对于 isSelected 属性,请使用不同的 Observable 链:
func isSelected(selectedVideo: Observable<Video>, onTapFavorite: Observable<Void>) -> Observable<Bool> {
selectedVideo
.map { $0.isFavorite ?? false }
.flatMapLatest { isFavorite in
onTapFavorite
.scan(isFavorite) { isFavorite, _ in !isFavorite }
.startWith(isFavorite)
}
}
绑定如下:
isSelected(selectedVideo: selectedVideo, onTapFavorite: favoriteButton.rx.tap.asObservable())
.bind(to: favoriteButton.rx.isSelected)
.disposed(by: disposeBag)
0赞
Daniel T.
7/8/2023
#2
在审查这个问题的要求时。我看到在考虑错误时可能会有很多复杂性。我决定给出一个答案,使用我的因果关系工具来充分充实答案,包括错误条件:
以下代码负责以下所有操作:
- 选择新视频时,(通过 )它会更新按钮的状态,而无需进行网络调用。
selectedVideo
- 当用户点击按钮时,它会更新按钮的状态,然后进行网络调用。如果调用失败,它会将按钮重置为以前的状态,并且对象会将错误通知错误订阅者(以防您想要发出警报或让用户知道发生错误的内容)。
api
- 如果用户在网络呼叫处于运行状态时点击该按钮,它将取消正在进行的呼叫,并为更新状态进行新的呼叫。
- (一个有趣的边缘案例)如果视频在网络调用进行时更新,然后网络调用失败,则不会使用旧的视频对象信息更新新的视频对象。
struct Video: Identifiable {
let id: Int
let isFavorite: Bool
func isFavoriteToggled() -> Video {
Video(id: id, isFavorite: !isFavorite)
}
}
func bind(favoriteButton: UIButton, selectedVideo: Observable<Video>, disposeBag: DisposeBag, api: API) {
enum Input {
case update(Video)
case networkFailure(Video)
case tap
}
let state = cycle(
input: Observable.merge(
selectedVideo.map(Input.update),
favoriteButton.rx.tap.map(to: Input.tap)
),
initialState: Video?.none,
reduce: { state, input in
switch input {
case .update(let video):
state = video
case .networkFailure(let video):
if state?.id == video.id {
state = video
}
case .tap:
state = state.map { $0.isFavoriteToggled() }
}
},
reaction: { action in
action
.compactMap { state, input in
guard case .tap = input else { return nil }
return state
}
.flatMapLatest { video in
(
video.isFavorite
? api.successResponse(.unfavoriteVideo(videoId: video.id))
: api.successResponse(.favoriteVideo(videoId: video.id))
)
.filter { !$0 }
.map { _ in Input.networkFailure(video) }
}
}
)
state
.compactMap { $0?.isFavorite }
.bind(to: favoriteButton.rx.isSelected)
.disposed(by: disposeBag)
}
extension Endpoint where Response == Void {
static func favoriteVideo(videoId: Video.ID) -> Endpoint { fatalError("create url request here") }
static func unfavoriteVideo(videoId: Video.ID) -> Endpoint { fatalError("create url request here") }
}
以上是我在我正在处理的项目中编写代码的方式。很有可能,我会将闭包拉出到可以测试的单独函数中。
评论