I am new to Rx and trying to one network call.
i mange to do that as follow:
struct Genre: Codable {
var genres: [Genres]?
}
struct Genres: Codable {
var id: Int?
var name: String?
}
final class GenreViewModel {
let disposeBag = DisposeBag()
var networking: Networking!
let getGeners = PublishRelay<[Genres]>()
var items: PublishRelay<[Genres]>?
var itemsDriver: Driver<[Genres]>?
let isLoading: Driver<Bool>
init(networking: Networking = Networking()) {
self.networking = networking
let shouldLoadItems = Observable.merge(
).debug("merge.debug(๐)")
.startWith(())
let gg = shouldLoadItems.flatMap {
networking.preformNetwokTask(
endPoint: TheMoviedbApi.genre,
type: Genre.self)
.debug("๐ network call")
}
isLoading = Observable.merge(
shouldLoadItems.map { true },
gg.map { _ in false }
)
.asDriver(onErrorJustReturn: false).debug("๐ is loading")
itemsDriver = gg
.map { $0.genres! }
.asDriver(onErrorJustReturn: []).debug("๐drive")
}
i am trying to figure a way of doing it without "let shouldLoadItems". something like that:
final class GenreViewModel {
let disposeBag = DisposeBag()
var networking: Networking!
let getGeners = PublishRelay<[Genres]>()
var items: PublishRelay<[Genres]>?
var itemsDriver: Driver<[Genres]>?
let isLoading: Driver<Bool>
init(networking: Networking = Networking()) {
self.networking = networking
let geners = getGeners
.flatMap { geners in
networking.preformNetwokTask(
endPoint: TheMoviedbApi.genre,
type: Genre.self)
.debug("๐ network call not in use")
}.startWith(())
.share()
isLoading = Observable.merge(
shouldLoadItems.map { true },
gg.map { _ in false }
)
.asDriver(onErrorJustReturn: false).debug("๐ is loading")
itemsDriver = geners
.map { $0.genres! }
.asDriver(onErrorJustReturn: []).debug("๐drive")
}
The vc:
func bindRx() {
viewModel.isLoading
.drive(hud.rx.animation)
.disposed(by: disposeBag)
viewModel.itemsDriver?.drive(collectionView.rx.items(cellIdentifier: GenreCollectionViewCell.reuseIdentifier, cellType: GenreCollectionViewCell.self)) { (row,item,cell) in
cell.config(item: item)
}.disposed(by: disposeBag)
}
Yet let geners not called. what am i missing out?
I expect all the questions in the comments was a bit of a pain, but the thing that was missing in this question was the "reactive" part of "functional reactive programming".
In FRP, a network request doesn't happen in isolation. It's usually triggered by an event producer and feeds an event consumer. In order for the request to happen at all, an event consumer must exist.
A view model, on its own and not connected to anything, does nothing. In order for your networking event to happen, something must be listening for the result. Your view model is like the plumbing in your house. The water doesn't flow unless someone opens a tap.
struct GenreViewModel {
let items: Observable<[Genres]>
let error: Observable<Error>
let isLoading: Observable<Bool>
init(networking: Networking) {
let result = networking.preformNetwokTask(endPoint: TheMoviedbApi.genre, type: Genre.self)
.compactMap(\.genres)
.materialize()
.share(replay: 1)
items = result
.compactMap { $0.element }
error = result
.compactMap { $0.error }
isLoading = result
.map { _ in false }
.startWith(true)
}
}
final class GenreViewController: UIViewController {
var tableView: UITableView!
var activityIndicatorView: UIActivityIndicatorView!
var viewModel: GenreViewModel!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.items
.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { _, genres, cell in
cell.textLabel?.text = genres.name
}
.disposed(by: disposeBag)
viewModel.error
.bind(onNext: presentScene(animated: true) {
UIAlertController(title: "Error", message: $0.localizedDescription, preferredStyle: .alert)
.scene { $0.connectOK() }
})
.disposed(by: disposeBag)
viewModel.isLoading
.bind(to: activityIndicatorView.rx.isAnimating)
.disposed(by: disposeBag)
}
}
In the above code, the network request will start as soon as the bind
call is executed. If an error emits, it is routed to the error
Observable and will display a UIAlertController with the error message (It uses the CLE-Architecture-Tools to do this.) If it succeeds, the genres objects' names will be displayed in the table view.