Reputation: 4760
I am working on an app that uses an API that have some inconsistencies, I have achieved a result with these 2 observables that perform some shared actions but the first one 'servers' is an array that binds to the UITableView.
serversViewModel.servers
.asObservable()
.observeOn(MainScheduler.instance)
.bind(to: serversTableView.rx.items(cellIdentifier: ServersTableViewCell.identifier, cellType: ServersTableViewCell.self)) { [weak self] (row, element, cell) in
guard let strongSelf = self else { return }
cell.serverProxy.accept(element)
if let currentServer = strongSelf.serversViewModel.currentServer.value,
element == currentServer,
let index = strongSelf.serversViewModel.servers.value.firstIndex(where: { $0 == currentServer }){
strongSelf.serversTableView.selectRow(at: IndexPath(row: index, section: 0), animated: true, scrollPosition: .top)
}
}
.disposed(by: disposeBag)
serversViewModel.currentServer
.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] (server) in
guard let strongSelf = self else { return }
if let server = server, let index = strongSelf.serversViewModel.servers.value.firstIndex(where: { $0 == server }){
strongSelf.serversTableView.selectRow(at: IndexPath(row: index, section: 0), animated: true, scrollPosition: .top)
}
else{
strongSelf.serversTableView.deselectAllItems(animated: false)
}
})
.disposed(by: disposeBag)
Is it possible to create a combined observable for both and use it for binding the UITableView?
Thank you
Upvotes: 1
Views: 3108
Reputation: 714
I'd approach from a slightly different way. First I would consider pulling the combine observable back into the ViewModel you have already structured. No need for this composition to be in your ViewController.
Then output that composed signal to bind into your rx.items. You can wrap your objects will a table cell view model to control whether to show them in a 'selected state'
Then also output the currentServer from your viewModel to simply scroll to it.
Upvotes: 1
Reputation: 33979
You want to use combineLatest
. Note that most of this should actually be in your view model...
In the below code, the servers
constant is a tuple of both the array of Server objects that should be displayed and the index path of the current server. Whenever either emits a new value, servers
will emit a value.
You might find the following article helpful in the future: Recipes for Combining Observables in RxSwift
let servers = Observable.combineLatest(serversViewModel.servers, serversViewModel.currentServer) { (servers, server) -> ([Server], IndexPath?) in
let indexPath = server.flatMap { servers.firstIndex(of: $0) }
.map { IndexPath(row: $0, section: 0) }
return (servers, indexPath)
}
servers
.map { $0.0 }
.bind(to: serversTableView.rx.items(cellIdentifier: ServersTableViewCell.identifier, cellType: ServersTableViewCell.self)) { (row, element, cell) in
cell.serverProxy.accept(element)
}
.disposed(by: disposeBag)
servers
.map { $0.1 }
.bind(onNext: { [serversTableView] indexPath in
if let indexPath = indexPath {
serversTableView.selectRow(at: indexPath, animated: true, scrollPosition: .top)
}
else {
serversTableView.deselectAllItems(animated: false)
}
})
.disposed(by: disposeBag)
Upvotes: 6
Reputation: 2582
There are several ways to combine observables in RxSwift. For your specific case, you will have to choose one that suits your needs best. Some of the operators are:
combineLatest
zip
merge
etc.
Read this documentation to get more idea about what each one it does.
Upvotes: 0