RxSwift 订阅者接收多个事件

RxSwift Subscriber receiving multiple events

考虑以下代码。

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

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

要解决您遇到的问题,您可以做的最简单的更改是将订阅移至 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