RxSwift 订阅者接收多个事件
RxSwift Subscriber receiving multiple events
考虑以下代码。
- 在 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()
}
}
响应式代码是声明式的。这是“设置”代码。所以它应该放在评论说“加载视图后做任何额外的设置”的地方。
要解决您遇到的问题,您可以做的最简单的更改是将订阅移至 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()
}
(请注意,Subject
s 应始终与 let
s 一起保存,永远不要 var
s。)
如果你使用 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
。
考虑以下代码。
- 在 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()
}
}
响应式代码是声明式的。这是“设置”代码。所以它应该放在评论说“加载视图后做任何额外的设置”的地方。
要解决您遇到的问题,您可以做的最简单的更改是将订阅移至 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()
}
(请注意,Subject
s 应始终与 let
s 一起保存,永远不要 var
s。)
如果你使用 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
。