Reputation: 57
Consider the below code.
isFetched
and then call fetchPopularMovies()
.fetchPopularMovies()
in turn calls an API. When the response is received, we will send OnNext(true)
event.Problem is, I receive multiple events on 2nd button tap onwards. If I add onCompleted(), I don't even receive events on 2nd button tap onwards. My expectation is that one event will be triggered on each button tap. What am I missing here?
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()
}
}
Upvotes: 1
Views: 1483
Reputation: 33979
Reactive code is declarative. It's "setup" code. So it should be placed where the comment says "Do any additional setup after loading the view."
The simplest change you can do to fix the problem you are having is to move the subscription into the viewDidLoad
method as Satish Patel referenced in his comment.
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()
}
(Note that Subject
s should always be held with let
s never var
s.)
If you use RxCocoa, you can simplify this code even more:
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)
}
}
Now, all of your code is "setup code" so it all goes in viewDidLoad
.
Upvotes: 0