RxSwift 订阅者接收多个事件

Posted

技术标签:

【中文标题】RxSwift 订阅者接收多个事件【英文标题】:RxSwift Subscriber receiving multiple events 【发布时间】:2021-04-22 01:55:59 【问题描述】:

考虑下面的代码。

    在 tapButton 上,我们订阅 Observable isFetched,然后调用 fetchPopularMovies()fetchPopularMovies() 依次调用 API。收到响应后,我们将发送OnNext(true) 事件。

问题是,我在第二个按钮点击后收到多个事件。如果我添加 onCompleted(),我什至不会在第二个按钮点击时收到事件。我的期望是每次点击按钮都会触发一个事件。我在这里错过了什么?

class ViewController: UIViewController 

    let popularMoviesURL = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=API_KEY")
    var isFetched = BehaviorSubject<Bool?>(value:nil)
    let disposeBag = DisposeBag()
        
    override func viewDidLoad() 
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    

    @IBAction func tapButton(_ sender: Any) 
        let observable = isFetched.asObservable()
        observable.subscribe(onNext:  observer in
            guard let result = observer else  return 
            print(result)
            print("onNext Recieved")
            
        , onError:  _ in
            print("onError Recieved")
        ).disposed(by: disposeBag)
        
        fetchPopularMovies()
    
    
    func fetchPopularMovies() 
        let task = URLSession.shared.dataTask(with: popularMoviesURL!) (data, response, error) in
            guard let _ = data else  return 
            self.isFetched.onNext(true)
           //self.isFetched.onCompleted()
        
        task.resume()
    

【问题讨论】:

因为您每次点击按钮都会订阅 订阅就像报纸,一旦订阅,您将获得该报纸的所有未来版本。如果您订阅该报纸两到五次,您将获得两到五份该报纸。 在你的情况下 distinctuntilchanged @SPatel 谢谢。我喜欢报纸的比喻。 【参考方案1】:

响应式代码是声明性的。这是“设置”代码。所以它应该放在评论说“加载视图后做任何额外的设置”的地方。

您可以做的最简单的更改来解决您遇到的问题是将订阅移动到 viewDidLoad 方法中,正如 Satish Patel 在他的评论中引用的那样。

override func viewDidLoad() 
    super.viewDidLoad()
    isFetched.subscribe(onNext:  observer in
        guard let result = observer else  return 
        print(result)
        print("onNext Recieved")

    , onError:  _ in
        print("onError Recieved")
    ).disposed(by: disposeBag)


@IBAction func tapButton(_ sender: Any) 
    fetchPopularMovies()

(请注意,Subjects 应始终与lets 一起使用,而不是vars。)

如果你使用 RxCocoa,你可以进一步简化这段代码:

class ViewController: UIViewController 
    let button = UIButton()
    let isFetched = BehaviorSubject<Bool?>(value:nil)
    let disposeBag = DisposeBag()

    override func viewDidLoad() 
        super.viewDidLoad()

        let popularMoviesURL = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=API_KEY")!

        let fetchedData = button.rx.tap
            .flatMapLatest 
                URLSession.shared.rx.data(request: URLRequest(url: popularMoviesURL))
                    .catch  error in
                        print("onError Recieved")
                        return Observable.empty()
                    
            

        fetchedData
            .map  _ in true 
            .bind(to: isFetched)
            .disposed(by: disposeBag)
    

现在,您的所有代码都是“设置代码”,所以它们都在 viewDidLoad 中。

【讨论】:

谢谢。移至 viewDidLoad() 并按预期工作。让我试试 RxCocoa。

以上是关于RxSwift 订阅者接收多个事件的主要内容,如果未能解决你的问题,请参考以下文章

观察者在单元格 RxSwift 中处理后仍然接收事件

Rxswift 在事件发生时取消观察者并重新订阅

RxSwift 对一个 observable 的多个订阅

RxSwift 共享订阅执行顺序

RxSwift之订阅UITableViewCell里的按钮点击事件

RxSwift - PublishSubject - 忽略错误并继续订阅(不要处置)