iosswiftrx-swift

How to use the Rxswift handler the favorite button


I have a favorite Button, the button isSelected changed by the network data and tap the button. When the isSelected by tap the button, it need to request the favorite or unfavorite API, but when it changed by the network data, it is not need to request api. It is my code, when the isSelected changed it always request the api.

// 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)

Solution

  • You have two features, so you should have two Observable chains...

    The server update chain looks like this:

    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) }
            }
    }
    

    And is bound like this:

    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)
    

    For the isSelected property, use a different Observable chain:

    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)
            }
    }
    

    which is bound like this:

    isSelected(selectedVideo: selectedVideo, onTapFavorite: favoriteButton.rx.tap.asObservable())
        .bind(to: favoriteButton.rx.isSelected)
        .disposed(by: disposeBag)