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

考虑下面的代码。

  1. 在 tapButton 上,我们订阅 Observable isFetched,然后调用 fetchPopularMovies()。
  2. 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 谢谢。我喜欢报纸的比喻。

标签: ios swift rx-swift


【解决方案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。
猜你喜欢
  • 2020-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 2019-11-03
相关资源
最近更新 更多